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.

Unix timestamps & UTC

Last refreshed 2026-09-17.