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.36k stars 223 forks source link

Timecop seems to have a bug when trying to strptime for February and the current day is 29, 30 or 31 #270

Closed miguelakira closed 3 years ago

miguelakira commented 3 years ago

After 0.9.2, I have noticed that I'm getting an error whenever I try to run strpftime for the month of February and the current day is 29, 30 or 31.

irb(main):007:0> Date.today
=> Thu, 29 Oct 2020
irb(main):008:0> Date.strptime('2018-02', '%Y-%m')
Traceback (most recent call last):
        1: from (irb):8
Date::Error (invalid date)

Since 2020 is a leap year, I tried Date.strptime('2020-02', '%Y-%m') while setting my system current date for the 29th of any month, and I got the expected correct result:

irb(main):001:0> Date.today
=> Thu, 29 Oct 2020
irb(main):002:0> Date.strptime('2020-02', '%Y-%m')
=> 'Sat, 01 Feb 2020'

But if I change the system date to the 30th, for example:

irb(main):006:0> Date.today
=> Fri, 30 Oct 2020
irb(main):007:0> Date.strptime('2020-02', '%Y-%m')
Traceback (most recent call last):
        1: from (irb):7
Date::Error (invalid date)

These errors seem to be related to these changes on 0.9.2:

lib/timecop/time_extensions.rb

d = Date._strptime(str, fmt) || Date.strptime_without_mock_date(str, fmt)
      now = Time.now.to_date
      year = d[:year] || now.year
      mon = d[:mon] || now.mon
      if d[:mday]
        Date.new(year, mon, d[:mday])
      elsif d[:wday]
        Date.new(year, mon, now.mday) + (d[:wday] - now.wday)
      else
        Date.new(year, mon, now.mday)
      end

When I reverted back to 0.9.1, things were ok and I had no errors being raised.

rocket-turtle commented 3 years ago

We had a similar Problem and ended up pinning the version to 0.9.1

joshuacronemeyer commented 3 years ago

Thanks for bringing this up, there seem to be quite a few bugs like this with the strptime implementation.

Just want to clarify someting. In your writeup you say that if you freeze the date to 10/29/2020 you expect Date.strptime('2020-02', '%Y-%m') to return Sat, 29 Feb 2020. When I run Date.strptime with %Y-%m I see the default ruby behavior is to return 2020-02-01, so i'd expect that to be the case. Am I missing something?

DylanAndrews commented 3 years ago

Wanted to add some strptime issues we are seeing in case it helps. When we do the following in the test env we get today's date (Mon, 02 Nov 2020), but we should get 2 months from now (Sun, 03 Jan 2021). Thanks.

time = 2.months.from_now.to_i
Date.strptime(time.to_s, '%s')
miguelakira commented 3 years ago

Thanks for bringing this up, there seem to be quite a few bugs like this with the strptime implementation.

Just want to clarify someting. In your writeup you say that if you freeze the date to 10/29/2020 you expect Date.strptime('2020-02', '%Y-%m') to return Sat, 29 Feb 2020. When I run Date.strptime with %Y-%m I see the default ruby behavior is to return 2020-02-01, so i'd expect that to be the case. Am I missing something?

You are correct, that's my mistake - it should return 2020-02-01, not 'Sat, 29 Feb 2020'. While copying and pasting I was deleting some extra stuff and must've left the result of Date.today instead of Date.strptime. I fixed my original comment.

joshuacronemeyer commented 3 years ago

I can't reproduce this issue since we merged in https://github.com/travisjeffery/timecop/pull/266 I am closing this as fixed and it will come out in the 0.9.3 release. The comment here from @DylanAndrews is a separate issue https://github.com/travisjeffery/timecop/issues/275 Would appreciate a PR for that.