Timezones without the pain
On this page
Timezones look like a UTC offset and a name. They are not. They are a political artifact wrapped in software that has accumulated decades of edge cases. If you’ve ever scheduled a recurring meeting that broke when one country changed DST and the other didn’t, you’ve already met the trap.
Here’s the working engineer’s guide to thinking about timezones — short version.
The IANA database is the authoritative source
The IANA Time Zone Database (sometimes called “tzdata” or “Olson database”) is the single source of truth that every modern OS ships. It contains the historical and current rules for every named timezone in the world.
Format of a zone name: Region/City — America/Los_Angeles,
Europe/London, Asia/Tokyo. The “City” is the most populous
representative city in that zone, not necessarily the only city.
Why “city” and not “country”? Because countries can have multiple zones (US has 6, Russia has 11) and zones can transcend borders.
When you write code that needs to handle “9 AM in New York”, you
should store the zone as America/New_York, not EST or UTC-5.
Because:
ESTis ambiguous (Australian Eastern Standard Time also exists).UTC-5doesn’t account for DST — half the year, “New York” is actuallyUTC-4.
DST is asymmetric across regions
US DST starts on the second Sunday of March, ends on the first Sunday of November. EU DST starts on the last Sunday of March, ends on the last Sunday of October. Australia is the opposite (their DST is during what the northern hemisphere calls winter). Some countries abandoned DST entirely (Russia stopped in 2014; Brazil in 2019).
The result: between March 8 and March 28 of any given year, the offset between New York and London changes from 5 hours to 4 hours back to 5 hours. A meeting scheduled “8 AM ET” can land at 1 PM, 12 PM, or 1 PM in London depending on the date.
This is why “9 AM ET” and “9 AM PT” can coexist in your calendar across two months and one of them silently shifts. Calendar apps that store the timezone (not the offset) handle this correctly. Apps that store an offset will produce “wait, when’s the meeting?” Slack messages every March and October.
Rule: store timezones, not offsets. Compute the offset at read time using the IANA database.
The Intl.DateTimeFormat API
Browsers ship the IANA database. You don’t have to install
anything; the API is Intl.DateTimeFormat:
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Tokyo",
hour: "numeric",
minute: "2-digit",
hour12: true,
});
fmt.format(new Date()); // "1:23 AM" (or whatever current time in Tokyo is)
This is the exact API our timezone converter uses. It handles DST automatically, including the asymmetric schedules.
For backend Node.js: same API, same behavior. For Python: use
zoneinfo (Python 3.9+) which reads the system tz database.
The half-hour and quarter-hour offset cases
Most timezones are at whole-hour offsets from UTC. A few are not:
- India is at
UTC+5:30(Asia/Kolkata). - Iran is at
UTC+3:30(most of the year; they had DST until 2022). - Newfoundland is at
UTC-3:30(and doesn’t sync DST with mainland Canada). - Nepal is at
UTC+5:45. Quarter hour. The only one in the world. - Chatham Islands (NZ) are at
UTC+12:45and observe DST.
If you’re building anything that displays time-zone offsets numerically, test with India, Newfoundland, and (if you’re feeling brave) Nepal.
The most common bug shape: an offset_hours INTEGER column. India
is 5.5. Your INTEGER truncates to 5. Indian users see times
30 minutes off.
The day-line problem
When converting “today” between hemispheres, the date itself can shift by a day. “9:00 AM in San Francisco on March 15” is “1:00 AM in Tokyo on March 16.”
Calendar apps that say “9:00 AM Tuesday” without naming the timezone are ambiguous when invitees span multiple zones. The timezone converter shows the date alongside the time, with a “yesterday” / “today” / “tomorrow” hint so you can spot the day-line.
Common scheduling pitfalls
A short list of the bugs we see most often:
- Storing local time, not UTC. A meeting at “9 AM” stored
without a zone is meaningless. Always store as a UTC instant +
an originating timezone, or as a zoned datetime
(
2026-05-04T09:00:00-07:00[America/Los_Angeles]). - Using offsets as if they were timezones.
UTC+9is fine today; in 2030 you can’t be sure (some country might shift to it for political reasons).Asia/Tokyois durable. - Round-tripping through
localtimestrings. Parsing"2026-05-04 09:00"as a Date is locale-dependent. Always use ISO 8601 with offset (2026-05-04T09:00:00-07:00) or UTC. - Recurring events that ignore DST. “Every Monday at 9 AM” in a meeting room means different UTC instants depending on the week. Most calendar systems get this right; some sync layers between calendar systems do not.
What our timezone tool does
The timezone converter implements:
- Pick a time-of-day (anchored to your local zone).
- See it in any number of cities, side-by-side, with date hints (yesterday/today/tomorrow).
- 24-hour day-night strip per row showing whether the recipient is in their working hours.
- IANA zone IDs internally, so DST and historical offsets are handled correctly.
Further reading
- IANA tz database
- tzdata commit log — every change to a country’s DST rules shows up here. It updates several times a year.
- Falsehoods programmers believe about time — exhaustive list of timezone-related foot-guns.
For the basic “what time is it in X” question, the converter is faster than thinking.