Today we came across an interesting issue related to the upcoming end of Daylight Savings Time here on the West Coast. We have an appointment calendar in our system that displays a user’s appointments for the upcoming two weeks in 30 minute blocks. The code takes a start date, in our case a Sunday and generates these time slots using Ruby’s relative date manipulation method calls on the DateTime object, like in this simplified example:
2.3.1 > start_date = DateTime.parse('2016-11-06 00:00:00 -0700') => Sun, 06 Nov 2016 00:00:00 -0700 2.3.1 > start_date.tomorrow => Mon, 07 Nov 2016 00:00:00 -0700
See anything out of the ordinary? Neither did we initially. You can debate it’s merits all you want, but here in San Francisco we still observe daylight savings time. And in 2016, Sunday the 6th of November at 2am is when it ends. One second after 1:59:59AM the clock rolls back to 1:00:00AM and boom, extra hour of sleep.
Now check out this output using the Time object:
2.3.1 > start_date = Time.parse('2016-11-06 00:00:00 -0700') => 2016-11-06 00:00:00 -0700 2.3.1 > start_date.tomorrow => 2016-11-07 00:00:00 -0800
Now do you see it? The problem with the output in the first example is the timezone offset, indicated above by the ‘-0700 ‘, meaning minus 7 hours from UTC. DateTime’s tomorrow method correctly advances the date but ignores the offset change imposed by the end of daylight savings time.
A quick glance at the documentation and low and behold, the second sentence says it all.
DateTime does not consider any leap seconds, does not track any summer time rules.
Well well well… there you have it. If you are writing code that needs to take daylight savings time or perhaps less commonly, leap seconds into consideration, you might have a better time with Time. Plus, the Time object has some other features that you might find useful as well, such as answering the burning question, are we observing daylight savings time today?
2.3.1 > start_date = Time.parse('2016-11-06 00:00:00 -0700') => 2016-11-06 00:00:00 -0700 2.3.1 > start_date.dst? => true 2.3.1 > start_date.tomorrow.dst? => false
So why have DateTime at all? Good question, DateTime is an extension of Date and does some things that Time cannot. One in particular is that it’s much better at handling historical calendar dates than Time is. I’ll send you elsewhere for the details, but the main thing you need to remember is that in order to measure time, you need a common reference point and agreed upon units (e.g. leap seconds). There is no single universal calendar that’s been used throughout time and place, take the Julian and Gregorian calendars for instance. This is where DateTime comes into play and you don’t have to look any further than the documentation on the DateTime object about when to use DateTime and when to use Time.