Time zones & UTC offsets
Why 'UTC+8' lies, what IANA zone names are for, and how to avoid the classic off-by-one date bugs.
Offsets are not zones
An offset like UTC+8 only says '8 hours ahead of UTC'. It does not tell you which region's rules apply β when daylight saving starts, or when the policy changes. Two places with the same offset today can diverge tomorrow.
π‘
Always store and transmit IANA zone names (e.g.
Asia/Shanghai, America/New_York), never bare offsets. Zones encode the full history and future of DST rules.Use ISO 8601 with the zone
2026-09-17T12:00:00Z (Z = UTC)
2026-09-17T20:00:00+08:00 (with explicit offset)
2026-09-17T08:00:00-04:00 (same instant, different zone)The trailing Z means UTC. Including the offset (or zone) makes the value unambiguous β essential for logs, APIs, and scheduling.
Common date bugs
- Doing date math in local time across a DST boundary (use UTC, or a tz-aware library).
- Assuming every day has 24 hours (DST spring-forward has 23).
- Formatting a UTC instant as a date without converting to the user's zone (off-by-one).
- Parsing non-ISO strings like
'09/17/2026'(is that Sept 17 or 9th of month 17?).
FAQ
Should I store timestamps or formatted dates?
Store a UTC timestamp or ISO-8601 UTC string. Format for display only, in the user's zone.
What library should I use?
Python: zoneinfo (stdlib). JS: Intl + a tz library for heavy work. Java: java.time. Avoid hand-rolled offset math.
Related
Last refreshed 2026-09-17.