This project is archived and is in readonly mode.
Time + Duration misbehaves on DST boundary
adding a Duration to a Time misbehaves, when the calculation crosses a DST boundary and the Duration is longer than 24 hours.
Essentially, it seems that 24.hour are treated as a full day, which gives incorrect (or at least unexpected) results if a day is only 23 hours long.
>> ENV["TZ"] = "CET" => "CET" >> Time.local(2009,3,29,0,0,0) # just before the change to DST => Sun Mar 29 00:00:00 +0100 2009 >> Time.local(2009,3,29,0,0,0) + 1.day => Mon Mar 30 00:00:00 +0200 2009 # midnight the next day, correct and as expected >> Time.local(2009,3,29,0,0,0) + 24.hours => Mon Mar 30 00:00:00 +0200 2009 # => WRONG, only 23 hours have passed because of DST switchover >> Time.local(2009,3,29,0,0,0) + 24.hours.to_i => Mon Mar 30 01:00:00 +0200 2009 # expected result
If the duration added is less than 24 hours the results are correct:
>> Time.local(2009,3,29,0,0,0) + 23.hours => Mon Mar 30 00:00:00 +0200 2009 # correct >> Time.local(2009,3,29,0,0,0) + 22.hours => Sun Mar 29 23:00:00 +0200 2009 # also correct
Using minutes instead of hours makes no difference
>> Time.local(2009,3,29,0,0,0) + (23 * 60 + 1).minutes # less than 24 hours => Mon Mar 30 00:01:00 +0200 2009 # correct >> Time.local(2009,3,29,0,0,0) + (24 * 60 + 1).minutes => Mon Mar 30 00:01:00 +0200 2009 # WRONG
this shows that the problem occurs on Durations of more than 24 hours:
>> Time.local(2009,3,28,23,0,0) + 23.hours => Sun Mar 29 23:00:00 +0200 2009 # correct >> Time.local(2009,3,28,23,0,0) + 24.hours => Sun Mar 29 23:00:00 +0200 2009 # wrong
Comments and changes to this ticket
There are unit tests in time_ext_test.rb that call for the incorrect behavior:
def test_daylight_savings_time_crossings_forward_start with_env_tz 'US/Eastern' do # st: US: 2005 April 2nd 7:27pm assert_equal Time.local(2005,4,3,19,27,0), Time.local(2005,4,2,19,27,0).since(86400), 'st+1.day=>dt' assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).since(86400), 'dt+1.day=>dt' end
Is Time#since really supposed to behave this way?
- State changed from new to open
- Milestone cleared.
I agree, this is wonky behavior. Reason it exists at all is because, in the days before Rails had a proper Duration class (i.e., 2006), this was a way to fake a duration of 1 day and have it cross a DST boundary correctly.
Now that we have Duration, it's not needed, it's confusing, and needs to go. Adding 24.hours should always advance the time exactly 24 hours, and adding 1.day should advance to the same time on the next day, which will be 23-25 hours later, depending upon if a DST boundary was crossed.
A patch for this would be welcome.
- Tag changed from active_support, duration, timezone to active_support, duration, patch, timezone
This should take care of it. I've updated the unit tests in time_ext_test.rb that were enforcing the incorrect behavior, and added a couple tests to duration_test.rb that test this specific case.
This is my first patch to rails.. if I did anything wrong please let me know!
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
Tickets have moved to Github
The new ticket tracker is available at https://github.com/rails/rails/issues