Hey there 😊

If you like what you read, feel free to …

🥗Buy me a salad
Share this 💬

DateTime add and subtract & daylight saving time

DateTime add and subtract & daylight saving time

The combination of daylight saving times and DateTime.add() 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?

1initializeDateFormatting('de');
2DateTime dateTime = DateTime(2021, 3, 28, 1); // Setting the DateTime to 2021-03-28, 1 AM
3print(dateTime.add(Duration(hours: 1)));

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 DateTime object.

The output is as follows:

12021-03-28 03:00:00.000

So far so good. The hour between 2 and 3 is missing, that’s why it’s skipped.

The issue

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.

1DateTime dateTime = DateTime(2021, 3, 29);
2 print(dateTime.subtract(Duration(days: 1)));

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:

12021-03-27 23:00:00.000

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 DateTime value.

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:

1DateTime dateTime = DateTime(2021, 10, 31);
2print(dateTime.add(Duration(days: 1)));

The output is:

12021-10-31 23:00:00.000

When navigating forward using the date chooser with dates normalized to 00:00, the user would be stuck when reaching this date.

The solution

Actually, the documentation of add and subtract warns us about this circumstance:

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 add() or subtract(). But what other options do we have?

Actually, the 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:

1DateTime dateTime = DateTime(2021, 10, 31);
2print(dateTime.add(Duration(days: 1)));
3print(DateTime(dateTime.year, dateTime.month, dateTime.day + 1, dateTime.hour, dateTime.minute));

The output is:

12021-10-31 23:00:00.000
22021-11-01 00:00:00.000

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:

1print(DateTime(2021, 11, 1).difference(DateTime(2021, 10, 31)));
2print(DateTime.utc(2021, 11, 1).difference(DateTime.utc(2021, 10, 31)));
125:00:00.000000
224:00:00.000000

Conclusion

When working with DateTime, it’s recommended to always use DateTime.utc if possible. If not possible, then avoid using the add and subtract method as it can lead to confusing results. Instead, use the DateTime constructor with the added time.

Comments (2) ✍️

Adnrew King

I’m trying to find the best way forward on this. Thanks for sharing your idea but I anticipate your solution will lead to problems on the last day of the month, and year.

strtotime in php has made me lazy. Seems a bit crazy to have to recode this situation in a reliable way.

Reply to Adnrew King

Marc
In reply to Adnrew King's comment

Hey Andrew,

thank you for your feedback! I agree this seems a bit off. However, I don’t understand why it would be a problem at the end of the year. Can you elaborate?

If I work with this method for the December 31, 2021, it works just as expected in the same way as .add( ):

1DateTime dateTime = DateTime(2021, 12, 31);
2print(dateTime.add(Duration(days: 1)));
3print(
4  DateTime(
5    dateTime.year, dateTime.month, dateTime.day + 1, dateTime.hour, dateTime.minute
6  )
7);

output:

12022-01-01 00:00:00.000
22022-01-01 00:00:00.000

Do I miss something?

Reply to Marc

Comment this 🤌

You are replying to 's commentRemove reference