What Unix epoch time actually is
Unix time, epoch time and POSIX time all name the same
thing: a count of seconds since 1970-01-01T00:00:00Z. It is deliberately the simplest
possible representation of an instant — one integer, no time zone, no calendar, no formatting decisions.
Right now that number is around 1.7 billion, and it grows by one every second.
The reason it dominates APIs, databases and log files is that it is unambiguous. A string like
03/04/2026 means March 4th to an American reader and April 3rd to almost everyone else, and
2026-03-04 09:00 means nothing at all until you know which zone it was written in. An epoch
timestamp has none of those problems: it names a specific moment that every system on earth agrees about.
Formatting into a human calendar is a display concern, and it happens as late as possible — ideally in the
user's own browser, where their zone and locale are actually known.
Because the value counts from UTC, it never needs a zone of its own. "The timestamp is in UTC" is a slight misstatement that nevertheless captures the right instinct: there is exactly one correct instant for a given epoch value, and rendering it in Tokyo or Denver changes the wall-clock text but not the moment being described.
Seconds or milliseconds — the single most common bug
Unix time is defined in seconds, but a great deal of software counts in milliseconds instead.
JavaScript's Date.now() returns milliseconds. Java's System.currentTimeMillis()
returns milliseconds. Kafka, most JVM logging, and a long list of JSON APIs return milliseconds.
Meanwhile date +%s, PHP's time(), Python's time.time() (as a float
of seconds), Postgres extract(epoch from ...) and every JWT claim in
RFC 7519 are in seconds. Mixing the two is the most frequent
timestamp bug in existence, and it has an unmistakable signature:
| Symptom | Cause | Fix |
|---|---|---|
| Every date renders in January 1970 | Seconds were passed to something expecting milliseconds | Multiply by 1000 |
| Dates land around the year 55,000 | Milliseconds were passed to something expecting seconds | Divide by 1000 |
| A token is always expired | An exp claim written in milliseconds | JWT claims are seconds — use seconds |
| A cache entry never expires | A TTL compared against the wrong unit | Normalise both sides at the boundary |
The digit count is the quickest diagnostic, and it stays reliable for a long time. A current timestamp in seconds has ten digits and will until November 2286. In milliseconds it has thirteen. Microseconds — which appear in Postgres, in some tracing systems, and in Chrome's internal timers — have sixteen, and nanoseconds have nineteen. The converter above uses exactly this heuristic, and the unit dropdown lets you override it when you are working with a very old or very distant date where the digit count would mislead.
The durable fix is a naming convention. A field called expires invites the bug; fields called
expires_at_s and expires_at_ms do not. Convert once, at the edge of your system,
and keep one unit internally.
UTC versus local time
An epoch value has no zone, but the moment you render it you have chosen one. That choice is where the second family of timestamp bugs lives. The tool above shows both renderings side by side precisely so the offset is visible rather than assumed.
Three rules cover most situations. Store UTC. Server clocks, databases and log pipelines
should all be in UTC, with no exceptions; a server whose local zone observes daylight saving will produce
an hour of duplicated timestamps every autumn and an hour that never happened every spring.
Transmit UTC. Send epoch integers or ISO 8601 strings with an explicit
Z or numeric offset; a bare 2026-07-20T09:00:00 is a guess waiting to be made
wrong. Render local. Convert to the viewer's zone in the client, where the zone is known.
Note that an offset is not a time zone. +02:00 tells you the difference from UTC at one
instant; Europe/Berlin tells you the rules, including when that offset changes. If you are
storing a future appointment — "9am on the first Monday of March next year" — an epoch value is
the wrong representation, because a government may move the daylight-saving boundary in the meantime and
the intended wall-clock time would shift. Store the local date, the local time and the IANA zone name, and
compute the instant when you need it. For everything that already happened, epoch is exactly right.
The year 2038 problem
Traditional Unix systems stored time in a signed 32-bit time_t. The largest value that holds
is 2,147,483,647, which arrives at 03:14:07 UTC on 19 January 2038. One second later the
counter overflows into negative territory and the date reads 13 December 1901. This is the direct
descendant of the Y2K problem, and unlike Y2K it is not a formatting issue that can be patched at the
display layer — it is the storage width itself.
Modern 64-bit platforms widened time_t long ago and are safe for roughly 292 billion years.
The exposure that remains is in the corners: 32-bit embedded devices and industrial controllers,
filesystem and network protocol structures with a fixed 32-bit time field, database columns declared as
INT rather than BIGINT, and binary formats that cannot be changed without
breaking compatibility. MySQL's TIMESTAMP type is a well-known example — it tops out in 2038,
while DATETIME does not.
The failure mode is already reachable today: any system computing a thirty-year expiry, a mortgage
schedule or a long-lived certificate can cross the boundary now. Press the
2147483647 button above to see the exact instant, and if your storage is a 32-bit integer,
that is the moment it stops working.
Leap seconds, and why the count is not really elapsed time
Earth's rotation is not perfectly regular, so UTC is occasionally adjusted by a leap second to stay in step with astronomical time. Twenty-seven have been inserted since 1972. Unix time, however, is defined such that every day contains exactly 86,400 seconds — which means a leap second has no representation in it at all. The counter simply repeats or stalls.
Two practical consequences. First, the difference between two Unix timestamps is not exactly the number of
seconds that physically elapsed between them if a leap second fell in between; the gap is short by one
second per leap. If you need true elapsed duration, use a monotonic clock
(performance.now(), CLOCK_MONOTONIC), never wall-clock timestamps — a fact that
also matters when timing requests in the API tester, where an NTP correction mid-request
could otherwise produce a negative duration. Second, major cloud providers now "smear" leap seconds,
spreading the adjustment over many hours so that no second is ever repeated. That keeps software happy at
the cost of the clock being deliberately, slightly wrong for a day.
ISO 8601, RFC 2822 and choosing a format
When a timestamp has to be human-readable as well as machine-readable, ISO 8601 is the
right default: 2026-07-20T09:12:00.000Z. It sorts lexicographically in chronological order,
it is unambiguous, it carries an explicit zone, and every language can parse it. RFC 3339 is the stricter
profile of ISO 8601 that internet protocols actually use, and it is what most JSON APIs mean when they say
"ISO date".
RFC 2822 — Sun, 20 Jul 2026 09:12:00 +0000 — is the older email and HTTP
style. You will still meet it in Date, Expires and Last-Modified
response headers, where the format is fixed by specification and always expressed in GMT. If you are
debugging caching behaviour, those header values and the epoch timestamps in your logs describe the same
instants, and the HTTP headers reference covers which header expects
which form. A mismatch there produces stale content or, when a clock is skewed far enough, a
412 Precondition Failed on a conditional request.
For anything that is not being read by a person, prefer the integer. It is smaller, it cannot be misformatted, and it does not tempt anyone into string comparison. Reserve formatted strings for the boundaries: logs, headers, and the screen.
Frequently asked questions
Is this epoch converter free and private?
Yes. Every conversion is arithmetic performed in this page. Nothing is uploaded, logged or stored.
Why does my date show as 1970?
You almost certainly passed seconds to something expecting milliseconds. A ten-digit value interpreted as milliseconds is about twelve days after the epoch. Multiply by 1000.
What is timestamp 0?
1970-01-01T00:00:00Z, the epoch itself. Seeing it in production usually means a field was
null or unset and got coerced to zero rather than being genuinely set to 1970.
Can I convert dates before 1970?
Yes — they are negative numbers, and this converter handles them. Be aware that some databases and libraries reject negative epoch values, which is why historical dates are often better stored as plain calendar dates.
How precise is a Unix timestamp?
One second by definition. Milliseconds, microseconds and nanoseconds are all conventional extensions that multiply the same base value, so precision is entirely a matter of which unit the producing system chose.
Related tools and reference
API Tester
Send a request and inspect the timestamps a real API returns.
JWT Decoder
Read the iat, nbf and exp claims — all epoch seconds.
Regex Tester
Extract timestamps from log lines before converting them.
Cron Expression Generator
Work out when a scheduled job will next run, in the same time-zone terms.
JSON Formatter
Pretty-print a payload to find the timestamp field you need.
HTTP Headers
Where Date, Expires and Last-Modified use RFC 2822 time.