Carrotly← back to field notes

15 March 20266 min readby Michael Cutler

What HeyIsla taught us about timezone math

Scheduling a meeting across timezones sounds like a solved problem until you actually try to do it across DST changes, Ramadan, and the international date line. Notes from the small team that built HeyIsla in Singapore (UTC+8) for people who mostly aren't.

HeyIsla is our small calendar assistant. You email Isla, she works out when a meeting should be, she sends the invites. The premise sounds boring until you sit down to write it, at which point you discover that "what time is the meeting" is one of those questions that gets harder the longer you stare at it. These are the notes we wish we'd had on day one.

Singapore is a deceptively easy place to write a calendar.

We built the first version in Singapore, where the offset from UTC has been a flat +8 hours since 1982. No daylight saving. No historical jitter that anyone living remembers. The local time on the wall today is the local time on the wall in six months. That sounds like a good thing, and for the engineers it is — every test we wrote in our own timezone passed.

It also makes you a bit of a liability when you start shipping to people who don't live here. Most of the world has a timezone that wobbles, sometimes twice a year, sometimes politically and without much warning. When the engineer's own clock is the boring one, it's very easy to write code that assumes everyone else's is too.

Our first paying customer was in Auckland. The second was in San Francisco. By month three we had a small bug that was, in retrospect, inevitable.

The bug that taught us to stop storing UTC.

The bug went like this. A customer in Auckland set up a recurring meeting for "every Friday at 7pm Auckland time". We did what the textbook says: convert to UTC, store the UTC instant, render back in the user's local time when needed. Tidy. Testable. Wrong.

On the first Sunday of April, New Zealand left daylight saving. Their wall-clock offset from UTC shifted from +13 to +12. Our stored UTC instant — say, 09:00 UTC — was still 09:00 UTC, but the local time it now corresponded to in Auckland was 21:00, not 19:00. Friday at 7pm had become Friday at 9pm. The host's calendar showed it wrong. The invitees got moved without anyone moving them.

The fix is the bit we hadn't appreciated. For a recurring event the user thinks of as wall-clock-anchored ("Friday at 7pm Auckland"), storing the UTC instant is the wrong primitive. What the user means isn't an instant in time — it's a rule. The rule says: at 7pm local time, in this specific IANA zone, on the days the rule applies. When DST changes, the rule still says 7pm local. The UTC value the next occurrence resolves to changes; the rule does not.

So now we store, for any recurring event, the local wall-clock time and the IANA zone (Pacific/Auckland, not "GMT+13"). We compute the UTC instant on the fly, per occurrence. One-off meetings are different — those are genuinely about an instant, and UTC is fine — but anything that recurs is wall-clock and zone.

Sounds obvious written down. It was not obvious from inside the bug.

What 'Wednesday at 4' actually means.

The other half of the work is on the input side. A human writes Isla a sentence: "let's do Wednesday at 4". Three words and several ambiguities:

  • Which Wednesday? "Next Wednesday" means different things on a Monday vs a Friday. "This Wednesday" said on a Wednesday is genuinely ambiguous in English. We've watched humans argue about it.
  • Which 4? Almost always 4pm; very occasionally 4am for a transcontinental sync. We default to the working-hours interpretation and ask if it lands at a weird hour.
  • In whose timezone? The asker's? The recipient's? The asker's primary calendar's? The asker's office's, even if they're travelling?

Our convention is: the asker's current timezone wins, but every confirmation email Isla sends shows the time twice — once in the asker's timezone, once in each invitee's timezone, with the date if it differs. That second part matters more than it sounds. If a meeting falls across the international date line, "Wednesday at 4pm Singapore" is "Wednesday at 1am New York" or "Tuesday at 8pm Honolulu", and getting the day wrong in an invite is a much worse failure than getting the time wrong. The day is the thing humans plan around.

The other thing we do is refuse to silently disambiguate. When the asker types "Wednesday at 4" and is currently in a city we haven't seen them schedule from before — say, they're travelling — Isla writes back asking. It feels chatty. It is much, much better than guessing.

What we use now.

After the Auckland incident and a few smaller ones, the rules we settled on:

  • Times are stored as wall-clock + IANA zone for recurring events, and as UTC instants only for one-offs.
  • IANA zone names always, never offsets. Europe/London is not the same as +01:00; the offset is a property of Europe/London at a specific moment, not a substitute for it.
  • IANA database updates are deployed promptly. When a country changes its rules — and they do, sometimes with a few weeks' notice — historical times you computed yesterday can correctly differ from the same calculation today. We track upstream and update.
  • Confirmation emails show date and time in every relevant timezone. Cheap; prevents the worst class of mistakes.
  • DST gaps and ambiguities are surfaced, not papered over. When a user proposes a time that doesn't exist on the day it would happen (because the clocks skipped forward over it), Isla says so and asks. When a time happens twice (the clocks fell back), same thing. The temptation to "just pick one" is strong and wrong.

None of this is novel; libraries like Luxon and Python's zoneinfo exist precisely because every team eventually learns it. What surprised us is how much of the work is in the human-facing surface — the wording in confirmations, the disambiguation questions, when to ask vs when to assume — and how little of it is in the database.

What we still get wrong.

A few things we haven't solved:

  • Ramadan and lunar calendars. Working-hours preferences shift during Ramadan in many parts of the world. We don't model that yet — customers tell us when, manually, and Isla respects it for that month.
  • The user who travels but doesn't tell us. If your laptop's timezone is set to Singapore but you've actually been in Lisbon for three weeks, we'll schedule in Singapore time. The fix is to ask, politely; we err on the side of not nagging.
  • Half-hour offsets. India (+5:30), Nepal (+5:45), parts of Australia. The maths is fine; the UX is harder, because dropdowns of round-hour slots are wrong for these users and we still default to them in some surfaces.

Calendars look like a solved problem from the outside. From the inside, they are a very long list of cases where the obvious answer is wrong for someone. Most of the work on HeyIsla in any given quarter is finding the next such case and writing it down before we forget. This post is one of those.

heyislacraftengineering

Was this useful? Email us a reply →

keep reading