Unix Timestamp Converter

When a log line carries a bare ten-digit number and the incident started at 3pm, the first job is turning one into the other without introducing a time zone error while you do it. Both readings are shown here so the offset is never left implicit.

Blank means now. A date with no offset is read as this browser's local time; add Z or +05:00 to be explicit.
Unix Timestamp Converter — Epoch Seconds and Milliseconds to Local Time and UTCBuildFigure

What the number is

Unix time counts seconds elapsed since 00:00:00 UTC on January 1, 1970, called the epoch. It carries no time zone, no offset and no calendar — it is a single integer naming an instant. That is exactly why it is the storage format of choice for logs, databases and APIs: the same moment is the same number in Denver and in Tokyo, and a time zone only enters when a human wants to read it.

Which means every timestamp bug is a display bug or a parse bug, never a storage bug. If two systems disagree about when something happened and both store epoch seconds, one of them converted at the wrong boundary.

Local time and UTC are not interchangeable, and this page shows both

The value at the top of the result is your browser's local time, labelled with the IANA zone it thinks it is in and its current offset. The UTC line beside it is the same instant in the reference zone. Neither is more correct; they are two renderings of one number. Which one you want depends entirely on who reads it next.

The trap sits in the input, not the output. A string like 2024-03-15T09:30:00 has no offset, and the ECMAScript specification says a bare date-time in that form is local time while a date-only string like 2024-03-15 is UTC. That inconsistency has produced a great many one-day-off bugs. This tool reads any offsetless input as local time and says so on the result, but the durable fix is upstream: never emit a timestamp without an offset. 2024-03-15T09:30:00-04:00 or 2024-03-15T13:30:00Z mean the same instant and neither can be misread.

American date order adds its own hazard when strings cross a border. 03/04/2024 is March 4 in the United States and April 3 nearly everywhere else, and no parser can tell which was meant. Both readings are valid dates, so nothing throws — the record is simply wrong. ISO order, year first, is unambiguous everywhere and sorts correctly as text, which is two good reasons to use it in anything machine-readable.

Seconds or milliseconds

Unix tools, Python's time.time(), Go's Unix() and PHP's time() deal in seconds, which is ten digits for any current date. JavaScript's Date.now() and Java's System.currentTimeMillis() deal in milliseconds, which is thirteen. Mixing them produces two very recognisable failure modes: a millisecond value read as seconds lands around the year 55,000, and a second value read as milliseconds lands in the third week of January 1970.

Auto-detection here treats anything at or above 1011 as milliseconds. In seconds, 1011 would be the year 5138, so there is no realistic overlap. A fractional value such as 1700000000.123 is read as seconds with the fraction preserved. Go's UnixNano gives nineteen digits and some tracing systems use microseconds — if you are pasting either, divide first, since neither is auto-detected.

The 2038 problem is not only about 2038

A signed 32-bit time_t tops out at 2,147,483,647, which is 03:14:07 UTC on January 19, 2038. One second later it wraps to negative and reads as December 1901. Modern 64-bit Linux has used a 64-bit time_t for years, and 32-bit builds got one in glibc 2.34, but the exposure is wider than the operating system: embedded controllers and industrial equipment with decade-long service lives, MySQL's TIMESTAMP column which cannot store past 2038-01-19 while DATETIME can, older filesystem metadata, and a good deal of firmware nobody plans to update.

The failures arrive early, which is the part worth internalising. Anything computing a date in the future — a thirty-year mortgage schedule, a certificate valid for twenty years, a retention policy, a far-future sentinel value — crosses the boundary long before the calendar does. Systems have been failing on this since roughly 2008.

Leap seconds, and the day that has 86,400 seconds no matter what

Earth's rotation is not perfectly regular, so UTC occasionally gains a leap second to stay aligned with astronomical time: 23:59:60 is inserted and the minute has sixty-one seconds. Twenty-seven have been added since 1972.

Unix time ignores all of them. It is defined as if every day contains exactly 86,400 seconds, which makes the arithmetic from a timestamp to a calendar date pure division with no table lookup — and means the mapping is not injective. During a leap second the same Unix value covers two real seconds. Most systems paper over this by smearing: Google and AWS spread the extra second across a window around the event so clocks stay monotonic and no timestamp repeats, at the cost of running very slightly wrong for several hours. Others step the clock and let it repeat.

For nearly all software this is irrelevant, and the simplification is what makes epoch time cheap to work with. It matters if you are timestamping financial trades, comparing measurements across systems that smear differently, or writing anything that assumes the difference between two timestamps is the true elapsed physical time. If you need genuine monotonicity, use a monotonic clock — CLOCK_MONOTONIC, performance.now(), System.nanoTime() — which is unaffected by leap seconds, NTP adjustments and a user changing the clock, and is the right tool for measuring a duration. Wall-clock time answers "when"; a monotonic clock answers "how long". Using one for the other is a bug waiting for an unusual day.

Questions people ask

The result says 1970. What went wrong?

A millisecond value was interpreted as seconds, or a value in another unit entirely got divided somewhere. Switch the unit selector to Seconds and see whether the date becomes plausible. The reverse symptom — a year in the tens of thousands — is a second value being read as milliseconds. Both are unit mismatches rather than bad data, and both are fixed at the boundary where the number crossed between two systems.

I entered a date and the result is off by several hours.

The input had no offset, so it was read as your browser's local time, and the UTC line differs by exactly your current offset. That is the correct behaviour and it is why both are shown. If you meant UTC, append a Z. If you meant a specific zone, append its offset, such as -05:00. A timestamp without an offset is ambiguous by construction and no tool can resolve it for you.

Why are the milliseconds always 000?

A second-resolution timestamp carries no sub-second information, so the milliseconds field is zero-filled on the way out. It is not precision that was lost in conversion; it was never in the input. If you need it, capture at millisecond resolution at the source — retrofitting it later means inventing it.

Is it safe to subtract two timestamps to get a duration?

Usually, with two caveats. Wall-clock time can move backwards when NTP steps the clock or a user changes it, so a duration measured that way can come out negative or wildly wrong; use a monotonic clock for anything you are timing. And because Unix time ignores leap seconds, the difference between two epoch values is not exactly the elapsed physical time across a leap second boundary — off by up to twenty-seven seconds for a span reaching back to 1972. For request latency and cache TTLs neither matters. For anything that has to reconcile against an external authority, both do.

Related