Unix Timestamps: Seconds, Milliseconds and the 2038 Problem
A Unix timestamp is a count of seconds since 00:00:00 UTC on 1 January 1970, a moment called the epoch. It is the most widely used way to represent a point in time in software, and it is popular for one reason: it is a single integer with no timezone, no locale, no formatting and no ambiguity.
1718409600 means the same instant everywhere on earth. 15/06/2024 does not
even mean the same day everywhere.
Seconds or milliseconds?
Unix time is defined in seconds. Plenty of platforms ignore this. JavaScript’s
Date.now() returns milliseconds. Java’s System.currentTimeMillis()
returns milliseconds. Python’s time.time() returns seconds as a float.
Mixing the two is the most common timestamp bug there is, and it fails in a memorable way: interpret a millisecond value as seconds and you land roughly fifty thousand years in the future. Interpret seconds as milliseconds and everything happened in January 1970.
Telling them apart is a digit count:
| Digits | Unit | Example | Resolves to |
|---|---|---|---|
| 10 | seconds | 1718409600 | June 2024 |
| 13 | milliseconds | 1718409600000 | June 2024 |
| 16 | microseconds | 1718409600000000 | June 2024 |
Ten digits will stay the norm for seconds until November 2286. Any all-digit timestamp you meet in the wild is almost certainly one of those three, and the length alone tells you which.
The Timestamp Converter detects the unit automatically and shows seconds, milliseconds, ISO 8601, UTC and your local time together, so you can confirm the conversion rather than assume it.
It is not quite “seconds elapsed”
Unix time is defined as seconds since the epoch excluding leap seconds, and this trips people up.
The earth’s rotation is slightly irregular, so UTC occasionally inserts a leap second. Unix time does not represent them. When a leap second occurs, a Unix clock either repeats a value or is smeared across a window by NTP.
The practical consequence: the difference between two Unix timestamps is not exactly the number of seconds that passed between them. Since 1972 the gap is 27 seconds and counting. For scheduling and logging this is irrelevant. For precise interval measurement it is not — use a monotonic clock for that, never wall-clock time.
The year 2038 problem
Stored in a signed 32-bit integer, the largest representable Unix timestamp is 2,147,483,647 — which arrives at 03:14:07 UTC on 19 January 2038. One second later the value overflows and wraps to negative, placing the system in December 1901.
This is the same shape of bug as Y2K, with a narrower blast radius. Modern 64-bit systems are unaffected; a 64-bit signed value does not overflow for about 292 billion years. The exposure sits in embedded devices, older file formats, and database columns explicitly declared as 32-bit integers.
It is also not purely a future problem. Any system doing date arithmetic more than a decade ahead — mortgage terms, certificate expiry, long-dated contracts — can overflow today while computing a date in 2038.
Timestamps versus ISO 8601
Both are unambiguous. They optimise for different readers.
Unix timestamps are compact, sort correctly as integers, and are trivial to do arithmetic on. They are unreadable to humans and carry no timezone context.
ISO 8601 (2024-06-15T00:00:00Z) is readable, sorts correctly as a string
when zero-padded and UTC, and records its own offset. It is more verbose and
easier to get subtly wrong by hand.
A reasonable default: Unix timestamps internally, ISO 8601 at API boundaries and in anything a person will read. Whichever you choose, store UTC and convert at the edge. Storing local time is how you end up with rows that duplicate or vanish when the clocks change.
Debugging timestamps in API responses
Timestamps rarely arrive alone. They are usually one field in a nested payload, which makes them awkward to inspect. Formatting the response first helps — the JSON Formatter makes the structure legible, and the API URL Extractor does the equivalent job for links.
All of these run in your browser, which matters when the payload is a real production response. The reasoning is in browser-based developer tools.
The short version
Count the digits before you convert: 10 is seconds, 13 is milliseconds. Store UTC. Use a monotonic clock for durations. And if anything in your stack stores time in a 32-bit signed integer, that is a real deadline, not a curiosity.
Related reading: Base64 encoding explained