This project is archived and is in readonly mode.

#405 ✓resolved
Laurent Farcy

next_week does not go to next week

Reported by Laurent Farcy | June 13th, 2008 @ 11:10 AM

Migrating from Rails 1.2.6 to 2.0.2, I found out an issue in a method that uses date calculations to start 3 months ago from now and stop today passing over every week. With 2.0.2, it never ended while it was running fine with 1.2.6.

After debugging the code, I realized next_week helper method does not go to next week on certain circumstances. Here's an example of the bug.

>> d = 3.months.ago.monday
=> Mon Mar 10 00:00:00 +0100 2008
>> d.next_week
=> Mon Mar 17 00:00:00 +0100 2008
>> d.next_week
=> Mon Mar 17 00:00:00 +0100 2008
>> d = d.next_week
=> Mon Mar 17 00:00:00 +0100 2008
>> d = d.next_week
=> Mon Mar 24 00:00:00 +0100 2008
>> d = d.next_week
=> Mon Mar 24 00:00:00 +0100 2008

If you do not calculate the date to start from with other helper methods, the bug is gone as illustrated below

>> d = DateTime.parse("Mon Mar 10 00:00:00 +0100 2008")
=> Mon, 10 Mar 2008 00:00:00 +0100
>> d = d.next_week
=> Mon, 17 Mar 2008 00:00:00 +0100
>> d = d.next_week
=> Mon, 24 Mar 2008 00:00:00 +0100
>> d = d.next_week
=> Mon, 31 Mar 2008 00:00:00 +0100
>> d = d.next_week
=> Mon, 07 Apr 2008 00:00:00 +0100

I was able to get the bug in another form by using tomorrow helper.

>>  d = 3.months.ago.monday
=> Mon Mar 10 00:00:00 +0100 2008
>> d.tomorrow
=> Tue Mar 11 00:00:00 +0100 2008
>> d = d.tomorrow
=> Tue Mar 11 00:00:00 +0100 2008
>> d = d.tomorrow
=> Wed Mar 12 00:00:00 +0100 2008
>> d = d.tomorrow
=> Thu Mar 13 00:00:00 +0100 2008
>> d = d.tomorrow
=> Fri Mar 14 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sat Mar 15 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sun Mar 16 00:00:00 +0100 2008
>> d = d.tomorrow
=> Mon Mar 17 00:00:00 +0100 2008
>> d = d.tomorrow
=> Tue Mar 18 00:00:00 +0100 2008
>> d = d.tomorrow
=> Wed Mar 19 00:00:00 +0100 2008
>> d = d.tomorrow
=> Thu Mar 20 00:00:00 +0100 2008
>> d = d.tomorrow
=> Fri Mar 21 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sat Mar 22 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sun Mar 23 00:00:00 +0100 2008
>> d = d.tomorrow
=> Mon Mar 24 00:00:00 +0100 2008
>> d = d.tomorrow
=> Tue Mar 25 00:00:00 +0100 2008
>> d = d.tomorrow
=> Wed Mar 26 00:00:00 +0100 2008
>> d = d.tomorrow
=> Thu Mar 27 00:00:00 +0100 2008
>> d = d.tomorrow
=> Fri Mar 28 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sat Mar 29 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sun Mar 30 00:00:00 +0100 2008
>> d = d.tomorrow
=> Sun Mar 30 23:00:00 +0200 2008
>> d = d.tomorrow
=> Mon Mar 31 23:00:00 +0200 2008
>> d = d.tomorrow
=> Tue Apr 01 23:00:00 +0200 2008

From these logs, I understand the root cause of all this is how date calculations (badly) handle savings time (transition from winter time to summer time). I had a look in the code but was unable to determine how to correctly patch it.

I was only able to reproduce this bug on Mac OS X 10.4, not Linux (Ubuntu) despite the fact that both computers were configured with the CEST timezone.

Comments and changes to this ticket

  • Laurent Farcy

    Laurent Farcy June 13th, 2008 @ 11:38 AM

    • Title changed from “next_week does not go to next week on os x” to “next_week does not go to next week”

    Actually, I was not able to reproduce the bug on Linux because I was trying with Rails 1.2.6 on this platform. I need to confirm the bug also exists there.

    Hopefully, there's a workaround for this bug. Add 2 hours evrytime you pass to next week.

    Loading development environment (Rails 2.0.2)
    >>  d = 3.months.ago.monday
    => Mon Mar 10 00:00:00 +0100 2008
    >>  d = d.next_week + 2.hours
    => Mon Mar 17 02:00:00 +0100 2008
    >>  d = d.next_week + 2.hours
    => Mon Mar 24 02:00:00 +0100 2008
    >>  d = d.next_week + 2.hours
    => Mon Mar 31 02:00:00 +0200 2008
    >>  d = d.next_week + 2.hours
    => Mon Apr 07 02:00:00 +0200 2008
    
  • Geoff Buesing

    Geoff Buesing June 13th, 2008 @ 04:22 PM

    • State changed from “new” to “incomplete”
    • Assigned user set to “Geoff Buesing”

    I wasn't able to replicate this in the console with Rails 2.1.

    Would you be able to provide a failing test case for this?

  • Laurent Farcy

    Laurent Farcy June 14th, 2008 @ 10:02 AM

    Geoff,

    Thanks for your reply. I was not able to reproduce the bug with 2.1.0.

    $ script/console
    Loading development environment (Rails 2.1.0)
    >> d = 3.months.ago.monday
    => Mon, 10 Mar 2008 00:00:00 UTC +00:00
    >> d = d.next_week
    => Mon, 17 Mar 2008 00:00:00 UTC +00:00
    >> d = d.next_week
    => Mon, 24 Mar 2008 00:00:00 UTC +00:00
    >> d = d.next_week
    => Mon, 31 Mar 2008 00:00:00 UTC +00:00
    >> d = d.next_week
    => Mon, 07 Apr 2008 00:00:00 UTC +00:00
    

    but can still confirm the bug with 2.0.2

    $ script/console
    Loading development environment (Rails 2.0.2)
    >> d = 3.months.ago.monday
    => Mon Mar 10 00:00:00 +0100 2008
    >> d = d.next_week
    => Mon Mar 17 00:00:00 +0100 2008
    >> d = d.next_week
    => Mon Mar 24 00:00:00 +0100 2008
    >> d = d.next_week
    => Mon Mar 24 00:00:00 +0100 2008
    >> d = d.next_week
    => Mon Mar 24 00:00:00 +0100 2008
    >> d = d.next_week
    => Mon Mar 24 00:00:00 +0100 2008
    

    The reason is date(time)s in 2.1.0 are timezone-agnostic (UTC) while they depend on the timezone defined for the local system in 2.0.x (+0100 which corresponds to CEST hereby).

    I don't know which timezone you live in but pretend you live in Paris, configure the timezone of your system accordingly and you should be able to reproduce the bug with 2.0.2.

    Like I previously mentioned, I strongly believed this defect is due to the daylight savings time which happened on 2008/03/30 in Central Europe. We 'lost' one hour in the night b/w 29th and 30th.

    Back to your request, giving you a failing and automated test case is not trivial since

    • the bug is system timezone dependent,
    • it seems to require that you first move to a date prior to the one when daylight savings time change happened by using time calculations primitives offered by ActiveSupport.

    I wish the manual test case described above is enough for you.

    Bottom line is this bug only exists under 2.0.x given that the local system is configured with a timezone that includes daylight savings time management.

    Surely not a show-stopper.

  • Geoff Buesing

    Geoff Buesing June 14th, 2008 @ 06:26 PM

    • State changed from “incomplete” to “resolved”

    Ok, I've looked into this a little more -- this is not an issue with 2.1 (irrespective of whether you're using config.time_zone or the system time zone), but it is indeed an issue with 2.0.x.

    The reason for the problem in 2.0.x is, Time#next_week calls Time#since with a Duration, which in 2.0.x doesn't behave properly when crossing a DST boundary.

    The fix for this issue was in: http://github.com/rails/rails/co...

    I'm hesitant to backport this fix to the 2.0 stable branch, given that Time#since is a lower-level method called by several other ActiveSupport methods.

    If you're not able to upgrade your app to 2.1, you could easily write your own version of Time#next_week that used Time#+ with a Duration, instead of Time#since, to work around this bug:

    def next_week(day = :monday)
              days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
              ((self + 1.week).beginning_of_week + days_into_week[day].day).change(:hour => 0)
            end
    
  • Geoff Buesing

    Geoff Buesing June 14th, 2008 @ 06:32 PM

    ...also, just for reference: if you want to write a test for a specific system time zone, you can set ENV['TZ'] for that test -- see the with_env_tz helper in ActiveSupport time_ext_test.rb.

    And if you need to test with a method that varies output depending upon system time (like 3.days.ago), you can always stub out Time.now -- we're doing this in several places in ActiveSupport time tests.

  • Laurent Farcy

    Laurent Farcy June 16th, 2008 @ 11:41 AM

    Geoff,

    I'm fine with not backporting the fix for the reason you mention and due to the fact I already found a workaround that works for me.

    Thanks for suggesting a fix I can setup in my project.

    For the test helpers around time calculations, now I know. I don't feel like it's useful that I contribute a test case now since the bug is already known, fixed and has already been added to next release.

    But I promess I'll come with an automated test next time I'll find something.

    Thanks for your help.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

People watching this ticket

Pages