The combination of daylight saving times and
DateTime.subtract() can cause unpredictable problems. Here’s what you can do about it.
Let’s start with an example: in Germany, we have moved our clocks one our forwards on 2021-03-28 at 2 AM. That’s an odd thing if you think about it: that Sunday only had 23 hours because the hour between 2 AM and 3 AM went missing.
An interesting question is: how does the
DateTime API behave, if we have a time before the “magic hour” and add some hours to it? Will it respect the DST or will it ignore it?
What we are doing is setting the time to one hour before the missing hour begins and add one hour using the
add() method of the
The output is as follows:
So far so good. The hour between 2 and 3 is missing, that’s why it’s skipped.
What if we are not skipping hours but days? Let’s imagine to have a date picker with two arrows, one incrementing the current day and one decrementing it.
A natural expectation when subtracting a whole day from
DateTime would be that the day of the date is decremented by 1 and the time stays the same.
The output when we subtract a
Duration of 1 day from the day after the day the clock was set one hour forward is as follows:
That does not meet any of the expectations. Neither the day was incremented by 1 nor the time stayed the same. Instead, 24 hours were subtracted which made the resulting
DateTime have a day that is decremented by 2 compared to the original
This is a tough problem in our fictional date chooser example as the user is unable to choose the date where the clock was set one hour forward. A quick solution could be to normalize every date by settings its time to 00:00. However, then the next problem arises when we look at the day it’s being set backwards:
The output is:
When navigating forward using the date chooser with dates normalized to 00:00, the user would be stuck when reaching this date.
Notice that the duration being added is actually 50 * 24 * 60 * 60 seconds. If the resulting DateTime has a different daylight saving offset than this, then the result won’t have the same time-of-day as this, and may not even hit the calendar date 50 days later.
Be careful when working with dates in local time.
So we may not use
subtract(). But what other options do we have?
DateTime constructor has a useful feature: if you provide an integer that exceeds the current time context such as a 32nd day of the month, it automatically switches to the first day of the next month.
Because of that we can do this:
The output is:
So the way of adding it by using the
DateTime constructor with the added days, produces the expected result.
It’s also possible to use the named constructor
DateTime.utc in the first place when working with dates. The documentation says:
When dealing with dates or historic events prefer to use UTC DateTimes, since they are unaffected by daylight-saving changes and are unaffected by the local timezone.
However, if the original time is e. g. based on the current date (
DateTime.now()), the user expects it to be the local time that is affected by daylight saving time.
The same issue arises when working with
DateTime.difference() as this example illustrates:
When working with
DateTime, it’s recommended to always use
DateTime.utc if possible. If not possible, then avoid using the
subtract method as it can lead to confusing results. Instead, use the
DateTime constructor with the added time.