DEV Community

calculatorspan
calculatorspan

Posted on

Why Your Date Shows 1970: Unix Timestamps in Seconds vs Milliseconds

Why Your Date Shows 1970: Unix Timestamps in Seconds vs Milliseconds

You fetch a timestamp from an API, pass it to new Date(), and the screen says January 20, 1970.

Or you get the opposite: a date in the year 55,840.

Nothing is wrong with your date library. You've met the most common timestamp bug there is: mixing up seconds and milliseconds.

What a Unix timestamp is

Need to decode a timestamp right now? Paste it into this free
Unix Timestamp Converter
and it shows the date for both seconds and milliseconds.

A Unix timestamp counts the time elapsed since 1970-01-01 00:00:00 UTC (the "epoch"). The catch is that different systems count in different units.

Unit Example Digits (today)
Seconds 1700000000 10
Milliseconds 1700000000000 13
Microseconds 1700000000000000 16
Nanoseconds 1700000000000000000 19

All four of these describe the same moment: 2023-11-14 22:13:20 UTC.

Who uses which unit
Seconds: Python's time.time() (as a float), PHP's time(), Go's time.Now().Unix(), most databases and JWT exp/iat claims, and many REST APIs
Milliseconds: JavaScript's Date.now() and new Date(ms), Java's System.currentTimeMillis(), and Go's UnixMilli()
Nanoseconds: Go's UnixNano() and many logging and tracing systems

The bug appears when a backend sends seconds and a frontend expects milliseconds, or the other way round.

Bug 1: seconds treated as milliseconds (you get 1970)

JavaScript's Date takes milliseconds. If you pass it seconds, it thinks only about 19 days have passed since the epoch:

js
const ts = 1700000000; // seconds, from an API

new Date(ts).toISOString();
// "1970-01-20T16:13:20.000Z" (wrong)

new Date(ts * 1000).toISOString();
// "2023-11-14T22:13:20.000Z" (correct)

Fix: multiply by 1000.

Bug 2: milliseconds treated as seconds (you get year 55,840)

Now the reverse. You have milliseconds, but the code multiplies by 1000 anyway:

js
const ts = 1700000000000; // milliseconds

new Date(ts * 1000).toISOString();
// "+055840-11-08T22:13:20.000Z" (wrong)

In Python, the same mistake raises an error instead of quietly giving a wrong date:

python
from datetime import datetime, timezone

datetime.fromtimestamp(1700000000000, tz=timezone.utc)

ValueError: year 55840 is out of range

datetime.fromtimestamp(1700000000000 / 1000, tz=timezone.utc)

2023-11-14 22:13:20+00:00 (correct)

Fix: divide by 1000 (Python, like most languages, expects seconds).

A quick way to tell which unit you have
If you just want to check one value without writing code, the
Unix Timestamp Converter
handles seconds and milliseconds.

Count the digits. For dates in the present day:

10 digits means seconds
13 digits means milliseconds

You can also guard against it in code:

js
function toDate(ts) {
// Below ~1e11 it's almost certainly seconds (1e11 s is the year 5138).
// Above it, treat as milliseconds (1e11 ms is March 1973).
return new Date(Math.abs(ts) < 1e11 ? ts * 1000 : ts);
}

toDate(1700000000); // 2023-11-14T22:13:20.000Z
toDate(1700000000000); // 2023-11-14T22:13:20.000Z

This heuristic is handy for display code and quick scripts. It fails for millisecond timestamps before March 1973, so in production code it's better to know the unit from the API docs and convert explicitly.

Getting the current timestamp in each unit
js
// JavaScript
Date.now(); // milliseconds
Math.floor(Date.now() / 1000); // seconds
python

Python

import time
time.time() # seconds (float)
int(time.time() * 1000) # milliseconds
go
// Go
time.Now().Unix() // seconds
time.Now().UnixMilli() // milliseconds
time.Now().UnixNano() // nanoseconds
Habits that prevent this bug
Name the unit in the field. Use createdAtMs or expiresAtSec instead of timestamp.
Document the unit in your API spec, and prefer one unit across the whole API.
Convert at the boundary. Parse incoming timestamps into a proper date type as soon as they enter your code, and avoid passing raw numbers around.
Store and compute in UTC. Convert to local time only when displaying.
Watch for 32-bit seconds. A signed 32-bit seconds value overflows on 2038-01-19 03:14:07 UTC. Use 64-bit integers for timestamps in new code.
A quick sanity check when you're debugging

When a value looks suspicious, paste it into a converter and see which unit gives a sensible date. I built a free Unix Timestamp Converter for exactly this, and there's a Time Zone Converter for when the date is right but the hour looks off.

Wrap-up
Unix timestamps count from 1970-01-01 UTC, but the unit varies.
1970 in your output usually means you passed seconds where milliseconds were expected.
A date in the year 55,000 usually means you passed milliseconds where seconds were expected.
Name your units, document them, and convert at the edges of your system.

What's the strangest timestamp bug you've run into? Share it in the comments.

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim •

The 13-digit timestamp in the JS example (1700000000000) clearly shows milliseconds, but the article doesn't mention how modern frameworks like Next.js handle this conversion automatically. I'd watch for when the Date.now() in server components gets misinterpreted as seconds in client-side APIs-especially with the new useServer pattern where timestamps might leak between layers.