Beware the perils of local time

Where were you at 2:15am (Sydney time) on 5 April 2009? Other than the fact that your memory is pretty hazy now that a few weeks have passed, you’d expect there to be an unambiguous answer to that question. However it turns out you could well have been in two completely different places at that time, as Sydney was coming out of daylight savings on that night, so the hour from 2am to 3am was repeated.

If you’re like me, you’ve probably known that this happens forever, but never really worried about it since you’re normally fast asleep at the time. However in the last few months I’ve been building an application that needs to keep an accurate record of when events happen (for example, in transaction and audit logs). In the early stages of development we recorded all of these events using local time, which was easy to build and easy for everyone to understand. However we realised this wasn’t a good idea when we first deployed the application to the target environment which is in a different time zone – all of the timestamps looked wrong when viewed from our time zone.

So we modified the application to record all timestamps in UTC. This was easy to do – basically we used DateTime.UtcNow everywhere we had previously used DateTime.Now. However we still wanted the application’s UI to show all timestamps in the users’ local time. Amazingly there was no way of doing this in .NET Framework 3.0 – it could convert between UTC and server time, but not between two arbitrary time zones. When you think about this more, it’s actually a very difficult problem as it requires knowing exactly when daylight savings starts at ends in any given year, and governments have a nasty habit of changing this from year to year. So the solution is to have a large historical database of daylight savings dates in every location in the world. Since .NET Framework 3.0 didn’t have this, we experimented with a few open source solutions but couldn’t find one that was reliable. Eventually we solved this problem by moving to .NET Framework 3.5, which has a wonderful class called TimeZoneInfo that does exactly what we need.

This all happened a few months ago, and I thought we were out of the woods. However last week we found one (hopefully) last surprise. Our application also includes some daily batch jobs that do some additional processing on the last day’s events. Even though our events were being recorded in UTC time, the batch job was scheduled to process events for a 24 hour period ending at the same local time every day. For 363 days a year this was fine, but there is one day a year with 25 hours (in which case we would have missed an hour), and one day a year with 23 hours (in which case we would process an hour twice). Theoretically we could have added some smarts to the application to deal with these days, but we decided the path of least resistance was to change the jobs to process 24 hours of events ending at the same UTC time (meaning the cut-of time will appear to “float” in local time as we move in and out of daylight savings).

Even though I’m a big fan of having an extra hour of sunlight on summer evenings, I’ve learned that daylight savings time (and indeed local time in general) can have a significant impact on applications that need to be precise about recording and processing timed events – even if your users aren’t scattered across time zones. The moral of the story is to store dates in UTC wherever possible, and to move to .NET Framework 3.5 to make conversions a snap.