Recently I learned that converting timestamps into UTC and saving it won’t work all the time. Especially when saving a future date.

TL;DR: You can jump to Jon Skeet’s original article that I learned this from. I wrote this blog post as a journal for better understanding and for future reference.

Future timestamps have to be always saved as the local date-time, instead of converting them into UTC and saving it. The future UTC that you calculated now can be different at that moment in the future.

The reason?

Countries can change their time zones, UTC offsets, and DST settings at any time — these all are political decisions. And it happens a lot around the world every year. For example, the USA will probably make DST permanent starting in 2023. Similarly, Mexico and Iran are considering abolishing DST. Syria and Jordan decided to remain in DST forever. Fiji didn’t start DST in 2021.

Here’s an Example

Imagine that there’s a meeting in India (UTC+0530), on 2024/01/01 5:30pm IST. Your application converts this timestamp into UTC and saves it as 2024/01/01 12:00pm UTC. Meanwhile, India decides to start DST in 2023 and fall back an hour at 02:00am, which makes the new offset to be UTC+06:30. On 2024/01/01 when the time hits 12:00pm UTC, which is now 6:30pm IST, the system sends a notification that the event starts now, but the actual event started one hour ago at 5:30pm IST. So you missed the event by 1 hour.

So, what’s the solution?

The easiest method is to preserve the local timestamp. Keep extra information like UTC for computational purposes. There’s this very popular, must-read article that explains the problem in detail and explores different options: “Storing UTC Is Not A Silver Bullet” by Jon Skeet.

Revisiting our example, we will keep the following data initially:

  1. Local timestamp: 2024/01/01 5:30pm
  2. Timezone: Asia/Kolkata (that is, UTC+0530)
  3. tzdata version: 2022d
  4. UTC: 2024/01/01 12:00pm (for notification purpose)

When there’s a new version of tzdata (say “2023b”) is available, we will update the above record like this:

  1. Local timestamp: 2024/01/01 5:30pm
  2. Timezone: Asia/Kolkata (now became: UTC+0630)
  3. tzdata version: 2023b
  4. UTC: 2024/01/01 11:00am (for notification purpose)

We are not done yet! There are situations like 01:30am IST occuring twice on the “fall back” day and 01:30am IST never occurring on the “forward” day. Jon Skeet’s article addresses many such issues, so I strongly recommend visiting that article.

How do software systems know the latest time zone updates?

IANA regularly publishes a database called tzdata that’s bundled with every OS and almost every programming language/library/framework/tool. The latest version is 2022d (2022-09-23). When you update your OS and tools, it will receive the latest version of tzdata.

For developers, you should check the tzdata version bundled with your runtime. For example,

  • Java bundles the latest tzdata with each release. Here’s a list.
  • Python will use the system tzdata until an additional timezone library is provided.
  • NodeJS uses the tzdata from the ICU library which is updated less frequently, so you may have to use external libraries like tzdata

So developers should always ensure that they use the latest OS, software, and tools when building applications that heavily make use of timestamps.