Unix Timestamps Explained: How Computers Store and Convert Time
Unix timestamps are the universal language of time in computing. Learn what a Unix timestamp is, how to convert it to a human-readable date, handle timezones, deal with Year 2038, and use the Temporal API.
What Is a Unix Timestamp?
A Unix timestamp (also called Unix time, POSIX time, or epoch time) is the number of seconds that have elapsed since the Unix epoch: midnight UTC on 1 January 1970.
Unix epoch: 1970-01-01T00:00:00Z = 0
Now (approx): 2026-04-01T12:00:00Z ≈ 1,743,508,800
Unix timestamps are timezone-agnostic — they always represent UTC. This makes them ideal for storage and transmission, because a timestamp means the same thing regardless of where you read it.
Milliseconds vs Seconds
Confusion arises because different systems use different precisions:
| System | Timestamp unit |
|---|---|
| Unix/POSIX, most databases | Seconds |
JavaScript Date.now() | Milliseconds |
| High-resolution APIs, performance APIs | Nanoseconds |
JavaScript is the common source of confusion. Date.now() returns milliseconds, so a current timestamp is around 1,743,508,800,000 — three extra digits compared to the Unix second value. Always check which unit you're working with.
// Seconds
Math.floor(Date.now() / 1000);
// Milliseconds (JavaScript native)
Date.now();
Converting a Timestamp in JavaScript
// Timestamp to Date
const timestamp = 1743508800;
const date = new Date(timestamp * 1000); // multiply by 1000 for milliseconds
date.toISOString(); // "2026-04-01T12:00:00.000Z"
date.toLocaleString("en-GB", { timeZone: "Europe/London" });
// Date to timestamp
const d = new Date("2026-04-01T12:00:00Z");
const ts = Math.floor(d.getTime() / 1000);
Timezones: The Tricky Part
Unix timestamps are UTC by definition, but displaying them to users requires timezone conversion. There are two common mistakes:
Mistake 1: Assuming local timezone
new Date(timestamp * 1000).toLocaleDateString() uses the system's local timezone, which differs between users and servers.
Mistake 2: Hardcoding a UTC offset
Timezones are not fixed offsets. They change for Daylight Saving Time, government decree, and historical revisions. Use a timezone name, not a raw offset:
// Correct: use named timezone
new Date(timestamp * 1000).toLocaleString("en-GB", {
timeZone: "Europe/London",
});
// Risky: UTC+1 is wrong during BST (UTC+1) vs GMT (UTC+0)
The IANA timezone database (bundled in modern browsers and Node.js via Intl) contains the correct rules for all named timezones.
The Year 2038 Problem
Unix timestamps are traditionally stored as a signed 32-bit integer, which can hold values from −2,147,483,648 to 2,147,483,647. The maximum value corresponds to 2038-01-19T03:14:07Z.
After this point, a 32-bit signed timestamp will overflow to a large negative number — the Unix equivalent of the Y2K bug.
Mitigation: Use 64-bit integers to store timestamps. Modern systems already do this — Linux kernel, macOS, most databases, and JavaScript's Date (which uses a 64-bit float) are not affected. Legacy embedded systems and old 32-bit databases are at risk.
ISO 8601: The Human-Readable Standard
When exchanging timestamps between systems (in APIs, databases, logs), use ISO 8601 format rather than Unix timestamps for readability:
2026-04-01T12:00:00Z — UTC (Z suffix)
2026-04-01T13:00:00+01:00 — BST (UTC+1)
2026-04-01T12:00:00.000Z — with milliseconds
ISO 8601 timestamps are unambiguous, sortable lexicographically, and widely supported in parsers.
The Temporal API (Modern JavaScript)
The Temporal API is a modern replacement for JavaScript's Date, handling timezones and calendars correctly:
// Get current instant
const now = Temporal.Now.instant();
now.epochSeconds; // seconds since epoch
// Convert to a local time
const london = now.toZonedDateTimeISO("Europe/London");
london.toString(); // "2026-04-01T13:00:00+01:00[Europe/London]"
// Parse from ISO 8601
const dt = Temporal.Instant.from("2026-04-01T12:00:00Z");
Temporal is at Stage 3 in the TC39 specification process and is available via a polyfill today.