A new trajectory: Trineo has been acquired by Traction on Demand. Learn More.

A lesson on time zones.

Pete Nicholls May 3, 2021

I had an unfortunate moment this morning. It started when I turned up to take a developer certification exam for a certain popular communications platform. To ensure there’s no cheating, they use a third-party who proctors the exam. Essentially they watch over a webcam and screen recording software as you take the exam.

When I turned up to take my exam, this proctor maintained that New Zealand time was an hour in the future. They said that I had turned up an hour late and was therefore a “no-show”.

As I have some time while I work this out with them, I’m going to explain the pernicious software bug that caused it. I’ll also explain how you can avoid making the same mistake in your own code.

What happened

Fifteen minutes before the exam was due to start, I logged in to prepare, and was confronted with the following message:

image

Strange. I double-checked my confirmation email, to make sure the date and time were correct:

image

They were. Concerned, I reached out to support. The exchange went something like this:

image

Followed by half an hour of slowly establishing that the time was in fact, 9:54 AM in Auckland. (Although by the time we established this it was more like 10:20 AM.)

Their website’s clock and my system clock, side by side.
Their website’s clock and my system clock, side by side.

As a software engineer, I recognised the problem straight away. Here’s my time zone settings:

image

Auckland, after daylight savings, is currently UTC+12:00. That means it’s 12 hours ahead of Coordinated Universal Time (UTC).

So the first issue is an obvious bug: the “Auckland, Wellington” time zone offset is incorrect.

The second issue is more subtle, and has to do with storing events in the future. When dealing with times in the past, it’s usually safe to normalise the time and store it in UTC. However, when storing dates in the future, you should never store UTC values.

Read on to learn why.

How they probably store meeting times

Here’s the interface for scheduling the exam:

image

You pick a date and a time in your time zone. You set your time zone like this:

image

The failure here is two-fold:

  1. Auckland time is not currently UTC+13:00 (it’s UTC+12:00)
  2. They probably store the time zone offset (UTC+13:00) instead of the time zone (Pacific/Auckland)

The result is a broken calculation:

  1. Take the given date and time (29 April 2021, 10 AM)
  2. Use the time zone offset to figure out the UTC date (subtract 13 hours)
  3. Store the UTC date

Here’s one way you could do this in Ruby:

image

Here’s a simplification of the database schema they likely use:

image

What’s the problem with this?

Offsets ≠ time zones

Offsets are not the same thing as time zones. A time zone’s offset can change through daylight savings shifts. “Auckland time” is sometimes UTC+12:00 and sometimes UTC+13:00.

Daylight savings rules can change unexpectedly

This may seem surprising, but daylight savings can change with little notice. Egypt is notorious for this: they have decided whether to scrap daylight savings days before it’s due to change.

Suppose you were scheduling lunch in Cairo next week. You convert the planned meeting time to UTC time based on an expectation that daylight savings will be observed. The next day, the Egyptian government decides that, in fact, they won’t hold daylight savings this year. Now, your time is incorrect and your lunch companion is left forlornly staring at the menu for an hour.

Removing the time zone destroys information

The simple test for this is that you cannot restore the time zone from a UTC-normalised date. Time zones are not simple additions or subtractions to UTC. Converting future dates to UTC destroys important information, introducing bugs around daylight savings and offsets.

How they should have stored times

First, the time zone setting. Rather than store the UTC offset, they should have stored the name of the time zone. The value I would use for this one from the list of IANA time zones (also known as the tz database):

image

But it would be even better if they could store the location of the event.

(Note that abbreviations are not suitable because they aren’t unique. Someone who lists their timezone as “CST”, for example, could be referring to Central Standard Time, China Standard Time, Australian Central Time, or Cuba Standard Time. It all depends who you’re asking.)

Here’s a better schema than the one above:

image

We can now figure out the time using the most up-to-date daylight savings rules.

In Rails:

image

Notice the correct time zone offset, +12:00.

In SQL, with Postgres' timezone function:

image

Takeaways

  • Don’t store future dates in UTC (past dates are okay)
  • Store IANA time zones along with the local time
  • Never underestimate the complexity of working with time
A hat we keep in the office as a warning for anyone writing software that deals with time zones!
A hat we keep in the office as a warning for anyone writing software that deals with time zones!
Pete Nicholls

Pete Nicholls