Epoch Units: Seconds, Milliseconds, Microseconds and the Off-by-1000 Bug
A Unix timestamp is a count since 1970-01-01T00:00:00Z. Nothing in the number says what it counts. The unit detector infers the unit from magnitude and prints which one it picked.
Magnitude table
| Unit | Today's value is about | Rule used |
|---|---|---|
| seconds | 1.79 × 10^9 (10 digits) | below 10^11 |
| milliseconds | 1.79 × 10^12 (13 digits) | below 10^14 |
| microseconds | 1.79 × 10^15 (16 digits) | below 10^17 |
| nanoseconds | 1.79 × 10^18 (19 digits) | anything larger |
The boundaries sit where the readings stop overlapping for any date you are likely to meet. 10^11 seconds is the year 5138; 10^11 milliseconds is March 1973.
Where each unit comes from
| Source | Unit |
|---|---|
JavaScript Date.now(), Java System.currentTimeMillis() | milliseconds |
Python time.time() (a float), PHP time(), date +%s | seconds |
Go time.Now().UnixNano() | nanoseconds |
Postgres extract(epoch from ...) | seconds, fractional |
What a wrong unit looks like
Feed a value to a reader that expects a different unit and the date lands somewhere recognisable:
| Mistake | Result |
|---|---|
| Seconds read as milliseconds | January 1970 (a 10-digit value is about 20 days of milliseconds) |
| Milliseconds read as seconds | Year 58000-ish; many tools show "Invalid Date" |
| Microseconds read as milliseconds | A date tens of thousands of years out |
Seeing 1970 almost always means a seconds value went through a milliseconds reader.
Fractional values
1789033200.5 is seconds with a fractional part and the detector reads it that way. Floating point cannot hold nanoseconds of a present-day timestamp exactly: a double has about 15–16 significant digits and a nanosecond epoch has 19.
The home-page rule
The home decoder uses a simpler rule (10 digits or fewer is seconds, otherwise milliseconds). It is right for the two common cases and wrong for microseconds and nanoseconds, which is what the detector is for.