Skip to content
DDevToolery

8 January 2025 · 5 min read

Seconds or milliseconds? Reading Unix timestamps correctly

Why the same number means 1970 in one system and next Thursday in another.

Unix time counts from midnight UTC on 1 January 1970. The specification says seconds. A great deal of software disagrees, and the disagreement is silent because both readings produce a valid date.

Telling them apart

Count the digits. A ten-digit number is seconds and lands somewhere between 2001 and 2286. A thirteen-digit number is milliseconds. If you read milliseconds as seconds you get a date tens of thousands of years in the future; if you read seconds as milliseconds you get a date in January 1970.

1716200000     // seconds      → 2024-05-20
1716200000000  // milliseconds → 2024-05-20

new Date(1716200000)      // 1970-01-20 — the classic mistake
new Date(1716200000000)   // 2024-05-20 — correct

Who uses which

  • Seconds: POSIX system calls, JWT exp/iat/nbf claims, Stripe, most Unix tooling, Postgres extract(epoch)
  • Milliseconds: JavaScript Date.now(), Java System.currentTimeMillis(), most JSON APIs written this century
  • Microseconds: Python's time.time_ns() derivatives, some tracing systems
  • Nanoseconds: Go's UnixNano, Prometheus internals

JWT is the trap worth memorising, because it sits between the two worlds. The exp claim is in seconds, but you compare it in JavaScript against Date.now(), which is milliseconds. Forget the multiplication and every token looks expired.

Time zones do not apply

A Unix timestamp has no time zone. It is a count of seconds since a fixed instant, identical everywhere on Earth. Time zones only enter when you format it for a human. That makes epochs an excellent storage and transport format and a terrible display format — store the instant, apply the zone at the edge.

The leap second detail

Unix time pretends leap seconds do not exist: every day is exactly 86,400 seconds. When a real leap second occurs, the counter repeats or skips a value. For almost all software this is invisible. If you are building something where a duplicated second matters, you need TAI rather than Unix time, and you already knew that.

2038

A signed 32-bit second counter overflows on 19 January 2038. Modern systems use 64-bit values and are fine, but embedded devices, old database columns and legacy file formats may not be. If you store timestamps as a 32-bit integer anywhere, that is worth finding now rather than in 2037.

Tools mentioned