1. Home
  2. Guides
  3. UTC vs Local Time: The Bugs It Causes

The difference between storing a moment and storing a wall clock

Most timezone bugs come from one mistake: storing a local wall-clock time and calling it a timestamp. Once a value is a moment in time, everything else is a display decision and stops being a source of corruption.

By Enrique M. · 6 min read · Last reviewed 2026-10-07

Store the moment, format the display

A timestamp is a point on the universal timeline. A local time is a wall clock reading in a particular place, and the same reading means different moments depending on when you look and where you are.

Store the moment. Convert to the local time only when rendering it for a person, and convert back using the timezone the person is actually in, not the one the server happens to run in.

// Store this: an absolute moment
2026-01-14T09:30:00.000Z

// Not this: a wall clock with no zone
2026-01-14 09:30:00

Daylight saving makes days the wrong length

On a spring-forward date, a day is 23 hours long. On an autumn day it is 25. Any code that assumes 24 hours per day is wrong twice a year, and usually wrong in a way that only shows up for a few weeks at a time.

The fix is to compute durations from real timestamps rather than from day counts. If you subtract two dates in milliseconds, you get the truth, including the transition.

  • A day is not always 86,400,000 milliseconds.
  • A month is not always 30 days, and not always 31.
  • "Add one month" from 31 January gives 28 or 29 February in most implementations.

The three formats worth knowing

ISO 8601 is what you store and what you send. RFC 3339 is the same thing with a slightly tighter grammar and it is the profile used by JSON. HTTP date is the format the web itself uses for dates in headers.

  • ISO 8601 — `2026-01-14T09:30:00.000Z`, for storage and transport
  • RFC 3339 — `2026-01-14T09:30:00Z`, for JSON payloads
  • HTTP date — `Wed, 14 Jan 2026 09:30:00 GMT`, for headers

The Unix epoch and the 2038 boundary

A Unix timestamp counts seconds since 1 January 1970 UTC. It ignores leap seconds, so it drifts slightly from true UTC, which matters for legally defined time and not for anything else.

If you store timestamps as a 32-bit signed integer, the year 2038 is the end of the world. Use a 64-bit field, and prefer storing an ISO string or a native date type if your database offers one.

What to take away

  • Store an absolute moment; convert to local time only for display.
  • A day is 23 or 25 hours twice a year — compute durations from timestamps.
  • Use ISO 8601 in storage, RFC 3339 in JSON, HTTP date in headers.
  • Never store a 32-bit timestamp if the data will outlive 2038.

Related tools