Unix Timestamp Converter: Epoch Time, Both Ways
Paste an epoch value and read the date, or pick a date and read the epoch value. The converter works out whether you handed it seconds, milliseconds, microseconds or nanoseconds — because getting that unit wrong is what breaks almost every timestamp you will ever debug.
The current Unix time
the classic Unix timestamp
what Date.now() returns
Read from your own device clock, so it is only as accurate as your machine is. Pause it if you need to copy a stable value.
Timestamp → date
Detected seconds from the magnitude (1,700,000,000).
Date → timestamp
| Unit | Detected when |value| is | Digits today | Where you meet it |
|---|---|---|---|
| Seconds (s) | |value| < 100,000,000,000 | 10 | Unix tooling, JWT exp/iat, cron, most REST APIs |
| Milliseconds (ms) | < 100,000,000,000,000 | 13 | JavaScript Date.now(), Java, Kafka, MongoDB ObjectIds |
| Microseconds (µs) | < 100,000,000,000,000,000 | 16 | Postgres internal time, Chrome traces, some tracing formats |
| Nanoseconds (ns) | ≥ 100,000,000,000,000,000 | 19 | Go time.UnixNano(), Prometheus/Grafana, InfluxDB |
The cut-offs are magnitude-based, not digit-count-based, so 0 and negative pre-1970 values are still read as seconds. Detection is a heuristic: a genuine millisecond timestamp from 1973 is smaller than 100,000,000,000 and will be read as seconds. Override the unit whenever you know what the source actually emits.
The year 2038 problem
A signed 32-bit time_t tops out at 2,147,483,647 seconds, which is 03:14:07 UTC on 19 January 2038. One second later it overflows to −2,147,483,648 and the clock reads 13 December 1901. That is why 64-bit time is now the standard: it pushes the limit roughly 292 billion years out. Anything still storing time in a 32-bit signed integer — embedded firmware, old database columns, legacy file formats — has a real deadline.
Unix time ignores leap seconds
Unix time is defined as if every day were exactly 86,400 seconds. Real UTC has had 27 leap seconds inserted since 1972, and a Unix timestamp simply repeats or smears a value across each one. So a Unix timestamp is not a true count of elapsed SI seconds since 1970 — it is a calendar encoding. Subtracting two timestamps across a leap second gives an interval that is off by a second. If you need genuine elapsed time, use a monotonic clock instead.
Every conversion here happens in your browser using its own date engine — nothing you paste is uploaded, logged, or sent anywhere. Values are shown in UTC unless a field says otherwise; the local column uses your device's time zone and its historical daylight-saving rules. Dates are rendered on the proleptic Gregorian calendar, so instants before 1582 will not match historical Julian records.
TL;DR
A Unix timestamp counts from 00:00:00 UTC on 1 January 1970, but nobody agrees on the unit. The unit is the bug. JavaScript and Java speak milliseconds; C, Unix and most APIs speak seconds; Go and Prometheus reach for nanoseconds. Mix them up and you land in January 1970 or somewhere past the year 50,000. Count the digits — 10 is seconds, 13 is milliseconds, 16 is microseconds, 19 is nanoseconds — then store UTC, keep the unit written down, and format only when you print.
Four units, one number, no label
An epoch timestamp is just an integer. It carries no unit, no time zone and no hint about which of them it is — so the only clue you get, before you paste it into something, is how long it is. That is enough. Each step up multiplies by a thousand, which adds three digits, so a present-day value has a distinctive length in each unit:
| Unit | Digits | Roughly, today | Where you meet it |
|---|---|---|---|
| Seconds | 10 | 1,78… × 109 | C’s time(), Python time.time(), JWT iat/exp, cron, Postgres extract(epoch from ts), most REST APIs |
| Milliseconds | 13 | 1,78… × 1012 | JavaScript Date.now(), Java System.currentTimeMillis(), Kafka record timestamps, most JSON APIs written by front-end teams |
| Microseconds | 16 | 1,78… × 1015 | PostgreSQL’s internal timestamp storage, Chrome trace files, ClickHouse DateTime64(6) |
| Nanoseconds | 19 | 1,78… × 1018 | Go time.Now().UnixNano(), InfluxDB line protocol, Prometheus and Grafana internals |
One footnote that catches people: PostgreSQL does store timestamps as 64-bit microseconds, but it counts them from 1 January 2000, not 1970 — a 946,684,800-second offset. You only see that in the binary wire format or in C extensions; extract(epoch from ts) gives you honest Unix seconds. Go’s nanosecond value has its own ceiling: a signed 64-bit nanosecond count runs out on 11 April 2262, which is why UnixNano() is documented as undefined outside that range.
1970 in one direction, the year 55,840 in the other
A unit mix-up has exactly two symptoms, and they are both loud enough to recognise on sight.
Seconds read as milliseconds divides your instant by a thousand. 1700000000 should be 14 November 2023; handed to new Date(1700000000) in JavaScript it becomes 20 January 1970. Every date in your app collapses into a three-week window at the start of 1970, which is why “why does everything say 1970?” is the single most-asked question about epoch time. The same screen appears for a completely different reason — a null, a 0 or an empty string coerced to zero — so check which one you have before you go hunting for a unit bug.
Milliseconds read as seconds multiplies it by a thousand instead. 1700000000000 parsed as seconds is somewhere in the year 55,840. This one is nastier than the 1970 case, because nothing crashes: most date libraries render the far-future year without complaint, so the value survives validation and quietly sorts to the end of time. Milliseconds read as microseconds is the same bug in miniature — everything lands in 1970 again, just a bit further in.
The practical defence is boring and it works: never write a bare timestamp field. Call it created_at_ms or expires_at_s, in the column name, in the JSON key, in the variable. A reviewer cannot see the unit in 1788739200, but they can see it in the name, and a name is the only place the unit can actually live. Paste anything ambiguous into the converter above — it auto-detects by magnitude and shows you all four readings at once, so you can spot the plausible one immediately.
Landmark timestamps worth recognising on sight
A handful of epoch values turn up constantly in logs, test fixtures and bug reports. Knowing them saves a conversion, and knowing the boundary ones saves an incident. All times below are UTC.
| Seconds | UTC date and time | Why it matters |
|---|---|---|
| -86,400 | Wed 31 Dec 1969, 00:00:00 | Timestamps can be negative. One day before the epoch. |
| 0 | Thu 1 Jan 1970, 00:00:00 | The epoch itself. Also what a null becomes. |
| 1,000,000,000 | Sun 9 Sep 2001, 01:46:40 | Timestamps became 10 digits here, and have stayed so. |
| 1,234,567,890 | Fri 13 Feb 2009, 23:31:30 | The classic test fixture. If you see it, it is fake data. |
| 1,500,000,000 | Fri 14 Jul 2017, 02:40:00 | A handy anchor for eyeballing whether a value is recent. |
| 2,000,000,000 | Wed 18 May 2033, 03:33:20 | Still five years short of the 32-bit ceiling. |
| 2,147,483,647 | Tue 19 Jan 2038, 03:14:07 | 231−1. The last second a signed 32-bit clock can hold. |
| 2,147,483,648 | Fri 13 Dec 1901, 20:45:52 | The overflow, one second later, on a 32-bit clock. |
| 4,294,967,295 | Sun 7 Feb 2106, 06:28:15 | 232−1. Where an unsigned 32-bit clock stops. |
The 2,147,483,648 row is not a real date — it is what a signed 32-bit machine displays once the counter has wrapped to −2,147,483,648. Paste it into the converter above and you will get 2038 instead, because JavaScript has no 32-bit ceiling to hit.
19 January 2038, 03:14:07 UTC
For decades Unix stored time in a signed 32-bit time_t. That holds 2,147,483,647 seconds — about 68 years past 1970 — and then the sign bit flips. One second after 03:14:07 UTC on Tuesday 19 January 2038, an unpatched 32-bit clock reads 20:45:52 on 13 December 1901. It is Y2K with better arithmetic behind it and far less publicity.
Most of the world has already moved on. 64-bit Linux has used a 64-bit time_t for years, 32-bit Linux gained one in kernel 5.6, and JavaScript never had the problem — its dates are doubles with a ±273,790-year range. What is left is the awkward tail: embedded firmware and industrial controllers with 20-year service lives, int columns in databases nobody wants to migrate, fixed-width binary file formats, and any code that stuffs a timestamp into an int32 on the way through a protocol.
The deadline arrives earlier than 2038 for anything that computes future dates. A system issuing 20-year certificates, mortgage schedules or retention policies overflows the moment it tries to represent a date past the ceiling, which for a 20-year horizon was January 2018. If you want to test your own stack, the tool above has a 2038 rollover example button that loads 2,147,483,647 for you.
Unix time is not a count of elapsed seconds
This one surprises even experienced engineers. Unix time is defined as if every day were exactly 86,400 seconds. Real UTC is not: because the Earth’s rotation does not cooperate with atomic clocks, 27 leap seconds have been inserted since 1972, the most recent at the end of 2016. Unix time does not count them. It repeats a value, skips one, or — on Google and AWS infrastructure — smears the extra second across a whole day so no clock ever has to show 23:59:60.
So a Unix timestamp is a calendar encoding, not a stopwatch reading. Subtract two timestamps that straddle a leap second and your interval is a second short of the physical truth. That is irrelevant for a “posted 3 hours ago” label and genuinely relevant for financial audit trails, scientific instrumentation and anything measuring latency.
If you need real elapsed time, do not use wall-clock timestamps at all — use a monotonic clock (performance.now(), time.monotonic(), CLOCK_MONOTONIC). It cannot go backwards when NTP corrects the machine, which wall-clock time absolutely can. The two are different instruments for different jobs: timestamps say when, monotonic clocks say how long.
Store UTC, format on display — there is no third option
An epoch timestamp has one great virtue: it has no time zone. It is a single point on the universal timeline, identical for every user on Earth, and that is exactly what you want in a database column, a log line and an API payload. Time zones are a presentation concern. Apply them at the last possible moment, in the user interface, using the viewer’s own zone.
Storing local time instead loses information you cannot recover. “2026-03-08 02:30” in New York never happened — the clocks jumped from 01:59 to 03:00 — and “2026-11-01 01:30” happened twice. Without an offset attached, those strings are unanswerable. Political changes make it worse: countries redefine their offsets and DST rules with a few months’ notice, so a local time stored today can mean a different instant after next year’s tzdata update. The one defensible exception is a future local commitment — a 09:00 dentist appointment in Lisbon should stay at 09:00 Lisbon time even if Portugal changes its rules — and for that you store the local time plus the zone name, not an offset and not a UTC instant.
The converter’s Date → timestamp panel makes the difference visible: switch between “your local zone” and UTC on the same wall-clock time and watch the epoch value move. For the wider argument, our guide to UTC vs GMT vs your time zone covers the offset arithmetic, and Unix timestamps explained works through the conversions in JavaScript, Python and SQL.
Looking for clock formats, not epoch numbers? This page is about machine time — the integer since 1970. If what you actually want is 14:30 turned into 2:30 PM, or a military-time chart, use the 24-hour to 12-hour time converter. Two different problems that share a word.
What this converter will not do
Worth saying plainly. It converts between UTC and your own device’s zone only— there is no picker for arbitrary time zones, so you cannot ask it for the epoch value of 9am in Tokyo unless Tokyo is where you are. Unit auto-detection is a heuristic based on magnitude, and it can be wrong: a genuine millisecond timestamp from 1973 is small enough to look like seconds, which is why the manual s / ms / µs / ns override exists. Hexadecimal input is rejected rather than guessed at. Dates are rendered on the proleptic Gregorian calendar, so instants before 1582 will not match historical Julian records. And there is no history, no saving and no account — everything runs in your browser, and nothing you paste is uploaded anywhere.
Frequently asked questions
Is my timestamp in seconds or milliseconds?
Count the digits. A present-day timestamp is 10 digits in seconds, 13 in milliseconds, 16 in microseconds and 19 in nanoseconds. If a value has 13 digits and your code treats it as seconds, the date lands tens of thousands of years in the future; if it has 10 digits and your code treats it as milliseconds, the date lands in January 1970.
Why does my date show as 1 January 1970?
Almost always because a value in seconds was passed to something expecting milliseconds, or because the value arrived as null, zero or an empty string that was coerced to 0. Epoch zero is exactly 00:00:00 UTC on 1 January 1970, so a screen full of 1970 dates is a unit or null bug, not a date bug.
What is the year 2038 problem?
A signed 32-bit time_t can hold at most 2,147,483,647 seconds, which is 03:14:07 UTC on Tuesday 19 January 2038. The next second overflows to negative and the clock reads 13 December 1901. Modern 64-bit systems are fine, but 32-bit embedded firmware, old database columns and fixed-width file formats still carry the bug.
Does a Unix timestamp include leap seconds?
No. Unix time is defined as if every day were exactly 86,400 seconds, so the 27 leap seconds inserted into UTC since 1972 are simply skipped, repeated or smeared. That means a Unix timestamp is a calendar encoding, not a true count of elapsed SI seconds, and subtracting two timestamps across a leap second gives an interval that is off by a second.
Should I store dates as timestamps or as strings?
Store an unambiguous instant — either an integer epoch value with the unit fixed and documented, or a timestamptz column that the database keeps in UTC. Convert to a local, human-readable string only at the moment you display it. Storing a formatted local string throws away the offset and the daylight-saving rules that made it meaningful.
Do you upload or store the timestamps I paste?
No. Every conversion runs in your browser using its own date engine, and nothing you type leaves the page. There are no accounts, no history and no server call. The live clock at the top reads your device clock, so it is only as accurate as your machine is.