Time zones, UTC offsets, and why you should store UTC
The difference between a UTC offset and a time zone, why daylight saving time makes offsets unreliable, and the rule that prevents most date bugs: store UTC, convert at the edges.
"What time is it in Tokyo?" sounds like one question with one answer, but software gets it wrong constantly — meetings land an hour off, timestamps drift after a daylight-saving change, a log says one thing and the database another. Almost all of it traces back to confusing two different things: a fixed offset from UTC, and a named time zone that changes its offset through the year. Getting the distinction straight fixes most date handling.
UTC is the reference
UTC (Coordinated Universal Time) is the global reference clock. It doesn't observe daylight saving and never shifts. Every other local time is expressed relative to it as an offset — Japan is UTC+9, New York is UTC-5 in winter. UTC is the anchor that makes "the same moment" expressible everywhere, which is why it's the thing you should store.
An offset is not a time zone
This is the core confusion. UTC+9 is an offset — a fixed number of
hours. Asia/Tokyo is a time zone — a named region with rules about
which offset applies and when. The difference matters because many
zones change their offset during the year:
America/New_Yorkis UTC-5 in winter and UTC-4 in summer under daylight saving time.Asia/Tokyois UTC+9 all year — no DST.
So storing "UTC-5" for a New York event is wrong half the year. Storing
America/New_York is correct, because the zone knows its own DST rules.
An offset is a snapshot; a time zone is the rulebook.
Daylight saving is where offsets break
DST is the single biggest source of time bugs because the offset changes on a date, and those transition days have surprises:
- In spring, clocks jump forward — a local time like 2:30 AM doesn't exist on the transition day.
- In autumn, clocks fall back — 1:30 AM happens twice, so a local timestamp is ambiguous.
A future meeting stored as a fixed offset will be an hour wrong on the other side of a DST change. Stored as a zone name plus local time, it stays correct because the conversion to UTC is computed with the rules in effect on that date.
The IANA time zone database
Zone names like Europe/London come from the IANA time zone
database (also called tz or zoneinfo), the shared dataset that maps
zones to their historical and current offset rules. It's updated
several times a year as governments change DST policy or offsets — which
is why keeping your runtime's tz data current matters. Note the naming:
zones are Region/City (America/Sao_Paulo), using a representative
city rather than a country, because a single country can span several
zones.
The rule that prevents most bugs
Store UTC. Convert to local time only at the edges — when displaying to a user — and store the user's time zone name, not an offset, when you need to reconstruct their local time later.
This one discipline avoids the entire class of problems. Internally everything is UTC, so comparisons, sorting, and arithmetic are unambiguous. Display-time conversion uses the zone's current rules. The exception is a future local event — a 9 AM dentist appointment next March — where you should keep the local time and zone name and resolve to UTC late, since the DST rules could even change before then.
If your timestamps are Unix epoch values, those are already UTC by definition — seconds since 1970-01-01 UTC, with no zone attached — a point worth understanding alongside Unix timestamps and the 2038 problem. To compare a moment across several zones at once, our world time tool shows the current time in multiple zones with their offsets, DST included, so you can see at a glance that "3 PM here" is the small hours somewhere else.