Skip to content
Dave Rolsky edited this page Jan 30, 2017 · 1 revision

Datetime math is giving me a headache.

Note doing subtraction of one datetime from another is pretty much the same thing as asking "how much time has passed between one datetime and the other?"

DST

Subtraction that crosses a DST change is weird. How do we account for that change? If we do subtraction on the local time, we end up with different answers than using the UTC time.

Here are some examples, all of which assume the America/Chicago time zone. For the years below, in this zone DST begins on the last Sunday of October and ends on the first Sunday in April.

Example 1

  2003-11-06T00:00:00  (DST is in effect, +1 hour offset)
- 2003-05-06T00:00:00  (DST not in effect)
=====================
  ???????????????????

There are two possible answers here. The "obvious" one is 6 months, but this ignores the DST change. The other answer is 6 months and 60 minutes, which accounts for the DST change between the two dates.

My guess is that most people would say 6 months, though, and that strikes me as correct (enough). Additionally, this answer works well the way DateTime.pm does addition:

  2003-05-06T00:00:00
+          6 months
=====================
  2004-11-06T00:00:00

Again, this is the "obvious" answer and seems to make sense.

Example 2

  2003-04-06T03:01:00  (DST not in effect)
- 2003-04-05T01:58:00  (DST is in effect, +1 hour offset)
=====================
  ???????????????????

Once again the subtraction is crossing DST. But what is the "obvious" answer now? I'm not sure. If you know one datetime is in DST and the other is not, then you might say 1 day and 3 minutes. If you didn't know that, then you'd say 1 day and 63 minutes.

Again, with the way DateTime.pm does math, the following is true:

  2003-04-05T01:58:00
+          1 day 3 minutes
==========================
  2003-04-06T03:01:00

This happens because DateTime.pm adds the date portion first, and then the time:

  2003-04-05T01:58:00
+          1 day
==========================
  2003-04-06T01:58:00
+                3 minutes
==========================
  2003-04-06T03:01:00

If it added minutes first, however, we'd get an error, since there is no 2:01:00 on 2003-04-06.

Another way to think of this is that on the day DST stops being in effect, the day is only 23 hours long, which is taken into account for subtraction.

Example 3

  2003-04-07T03:01:00  (DST not in effect)
- 2003-04-05T01:58:00  (DST is in effect, +1 hour offset)
=====================
  ???????????????????

The subtraction is still crossing DST.

DateTime.pm says this is true:

  2003-04-05T01:58:00
+          2D    3m
==========================
  2003-04-07T02:01:00

Again, this happens because DateTime.pm adds the date portion first, and then the time:

  2003-04-05T01:58:00
+          2 days
==========================
  2003-04-07T01:58:00
+                3 minutes
==========================
  2003-04-07T02:01:00

Example 4

  2003-10-26T01:00:00  (DST is in effect, +1 hour offset)
- 2003-10-26T01:00:00  (DST not in effect)
=====================
              1 hour

This is quite obvious, but worth remembering. In this case we have a 25 hour day so the same clock time shows up twice, an hour apart. We want DateTime.pm to get this right too.