travisjeffery / timecop

A gem providing "time travel", "time freezing", and "time acceleration" capabilities, making it simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
MIT License
3.35k stars 224 forks source link

`strptime` incorrectly parses the date #132

Closed dmitry closed 2 years ago

dmitry commented 9 years ago

I have following situation:

Date.strptime("11-01-08", '%Y-%m-%d')
# Thu, 08 Jan 0011
require 'timecop'
# true
Date.strptime("11-01-08", '%Y-%m-%d')
# Sat, 10 Jan 0011

Even Date::ITALY works as it should:

Date.strptime("11-01-08", '%Y-%m-%d', Date::ITALY)
# Thu, 08 Jan 0011
yaauie commented 9 years ago

Very odd, I'm getting odd but different results here:

Date.strptime("11-01-08", '%Y-%m-%d').strftime
# => "0011-01-08"
require 'timecop'
# => true
Date.strptime("11-01-08", '%Y-%m-%d').strftime
# => "0011-01-10"
Date.strptime("11-01-08", '%Y-%m-%d', Date::ITALY).strftime
# => "0011-01-10"

I would wager that it's because Ruby Time objects are backed by a float representing the offset from 1970-01-01T00:00:00Z, and that the year 0011 is far enough away that there is a loss of precision.

dmitry commented 9 years ago

Any ideas how to fix that issue(s)?

PS. I have ruby 1.9.3

dmitry commented 9 years ago

Looks like another issue reported is here: https://github.com/travisjeffery/timecop/issues/116

sos4nt commented 7 years ago

It happens because Timecop uses Time#to_date in lib/timecop/time_extensions.rb#L62.

Time#to_date treats the input as proleptic Gregorian calendar date values, i.e.:

Time.strptime("11-01-08", '%Y-%m-%d').to_date
#=> #<Date: 0011-01-10 ((1725085j,0s,0n),+0s,2299161j)>

is equivalent to: (the unpatched)

Date.strptime("11-01-08", '%Y-%m-%d', Date::GREGORIAN).new_start
#=> #<Date: 0011-01-10 ((1725085j,0s,0n),+0s,2299161j)>
daveallie commented 7 years ago

Any update on whether this issue will be resolved?

Annoyingly, including timecop in a test suite means that tests can pass, even though the dates being parsed could be out by a few days. @sos4nt's answer here states that attempting to parse and dates before 04/10/1582 will lead to discrepencies.

lisbethmarianne commented 5 years ago

We see this:

irb(main):001:0> week = '201810'
=> "201810"
irb(main):002:0> Date.strptime(week, "%Y%W")
=> Mon, 05 Mar 2018
irb(main):003:0> require 'timecop'
=> true
irb(main):004:0> Date.strptime(week, "%Y%W")
=> Mon, 01 Jan 2018
dmitry commented 5 years ago

Solution is here: https://github.com/travisjeffery/timecop/pull/217

kwerle commented 5 years ago
    it "should accept dates prior to 1900s with century information (e.g. 6/1/0007)" do
      expect(ActiveRecord::Type::Date.new.type_cast_from_user("6/1/0007")).to eq(Date.new(7, 6, 1))
    end

with Timecop:

  1) ActiveRecord::Type::Date#type_cast_from_user(value)·default locale·should accept dates prior to 1900s with century information (e.g. 6/1/0007)
    Failure/Error: expect(ActiveRecord::Type::Date.new.type_cast_from_user("6/1/0007")).to eq(Date.new(7, 6, 1))

      expected: Wed, 01 Jun 0007
           got: Fri, 03 Jun 0007
dmitry commented 5 years ago

@kwerle please check the pull request #217, probably this one should work in your case.

kwerle commented 5 years ago

Submitted a new pr: #242. Fixed tests (mostly Gemfile updates).

spalenza commented 4 years ago

I changed to use DateTime with to_date instead of Date.

 DateTime.strptime("11-01-08", '%Y-%m-%d').to_date
 => Thu, 08 Jan 0011
joshuacronemeyer commented 2 years ago

I'm closing this issue because the original behavior mentioned has changed. It doesn't seem to be a bug anymore. I have created a new issue for the behavior that @lisbethmarianne mentioned in her comment tho. It can be found here https://github.com/travisjeffery/timecop/issues/380