AZ Tools
← Guides

Working with Time Zones and Unix Timestamps Without Bugs

Dates and times cause a disproportionate number of bugs, because what feels obvious to a human is genuinely complicated for a computer: time zones shift, clocks skip an hour twice a year, and 'today' depends on where you are standing. This guide explains the core ideas and gives you a handful of rules that head off most date-handling mistakes.

Store time as an absolute instant

The single most important rule: store and transmit moments as an absolute, time-zone-independent instant — a Unix timestamp (seconds or milliseconds since 1 January 1970 UTC) or an ISO 8601 string in UTC (ending in Z). An instant is the same everywhere on Earth; only its human representation changes.

Convert to a local time zone only at the very edge, when you display the value to a person. Keep the inside of your system in UTC. Most painful date bugs come from storing a 'local' time without recording which zone it was local to, leaving the value ambiguous forever.

UTC, offsets and time zones are not the same thing

UTC is the global reference clock. An offset like +09:00 is a fixed shift from UTC at one moment. A time zone, such as America/New_York, is a named set of rules describing which offset applies at every point in history — including when it changes.

The distinction matters because offsets change within a single zone. New York is -05:00 in winter and -04:00 in summer. Storing only an offset loses the rule, so you can't correctly compute a future local time. This is why scheduling 'every day at 9am New York time' needs the zone name, not a fixed offset.

Daylight Saving Time breaks naive assumptions

Twice a year, zones that observe DST jump. In spring an hour is skipped, so a local time like 02:30 simply does not exist that day; in autumn an hour repeats, so 01:30 happens twice and is ambiguous. Code that assumes every day has 24 hours, or that a given local time always exists, will eventually produce wrong results or crash.

Working in UTC sidesteps this entirely, because UTC has no DST. You only confront the gaps and overlaps when converting to local time for display or when interpreting human input — which is exactly where a tested date library should do the work rather than hand-rolled arithmetic.

Seconds, milliseconds, and the year 2038

Unix timestamps come in two common units and mixing them is a frequent bug: most Unix tools and APIs use seconds, while JavaScript's Date works in milliseconds. A value that is off by a factor of 1000 usually means the units were confused — a timestamp suddenly in the year 56000, or back in 1970.

There is also a size limit: a Unix time stored in a signed 32-bit integer overflows on 19 January 2038. Modern systems use 64-bit values, which removes the problem for any practical purpose, but it's worth knowing why old systems mishandle far-future dates.

Rules that prevent most bugs

None of this requires deep expertise day to day — a few habits cover the vast majority of cases.

  • Store and exchange instants in UTC (Unix timestamp or ISO 8601 with Z); convert to local only for display.
  • Keep the IANA zone name (e.g. Europe/Paris) when a future local time matters; an offset alone is not enough.
  • Never do calendar math by adding fixed numbers of seconds for 'a day' or 'a month' — use a date library that knows about DST and month lengths.
  • Be explicit about seconds vs milliseconds at every boundary, and label which you mean.

Related tools