What a Unix timestamp actually is
A Unix timestamp is a count of seconds elapsed since 00:00:00 on 1 January 1970, Coordinated Universal Time. That instant is called the epoch, and the number that counts from it is deliberately stupid: no timezone, no calendar, no daylight saving, no locale, no formatting. It is a position on a line.
The stupidity is the whole point. Two servers on opposite sides of the planet agree on
1700000000 without agreeing on anything else — not on a date format, not on
which day the week starts, not on whether the clocks moved last Sunday. Every
complication people associate with dates lives in the rendering of that number,
never in the number itself.
Which leads to the sentence that prevents most date bugs, if you internalise it: a timestamp is an instant, a date is a description of one. "14 November 2023, 19:13" is not an instant until somebody says where. The same wall clock reading is a different moment in São Paulo and in Lisbon. The number is unambiguous; the sentence never is.
Seconds or milliseconds: the bug that reaches production
This is the number one reason people end up on a page like this one, and it is nastier
than it looks. 1700000000 is November 2023. 1700000000000 is
also November 2023 — the same instant, expressed in milliseconds. Neither one
throws an error. Feed the millisecond value to something expecting seconds and you land
in the year 55834; feed the second value to something expecting milliseconds and you land
on 20 January 1970. Both are wrong, both parse cleanly, and neither triggers an alarm
until a customer notices a subscription that expires 53 thousand years from now.
The reason the mix-up is so easy is that the ecosystem never agreed on a unit. A short, incomplete list of what the tools around you actually return:
| Source | Unit | Digits today |
|---|---|---|
JavaScript Date.now() | milliseconds | 13 |
Java System.currentTimeMillis() | milliseconds | 13 |
Python time.time() | seconds (float) | 10 |
Go Time.Unix() | seconds | 10 |
PHP time() | seconds | 10 |
PostgreSQL extract(epoch from …) | seconds | 10 |
JWT exp and iat claims | seconds | 10 |
| Kafka record timestamps | milliseconds | 13 |
| Stripe API | seconds | 10 |
MongoDB ObjectId prefix | seconds | 10 |
Note the pairing that causes most real incidents: a JavaScript front end that thinks in
milliseconds talking to a Python or Go back end that thinks in seconds, with a
thousand-fold error hiding in a single multiplication nobody wrote. JWT is the classic —
exp is defined in seconds by the spec, and passing Date.now()
straight into it produces a token that is valid for the next fifty millennia.
This converter detects the unit from the order of magnitude: below 1011 it reads seconds, up to 1014 milliseconds, above that microseconds. Those thresholds are chosen because a real timestamp never falls in the overlap — 1011 seconds is the year 5138. The detection is shown on screen rather than applied silently, and you can override it, because there is one case the magnitude cannot resolve: a millisecond value from before March 1973 looks exactly like a second value from the far future. Guessing silently there would be worse than not guessing at all.
Timezones: the second trap
Once the unit is right, the timezone is what is left to get wrong. Converting a timestamp to a date always needs a timezone — the number is UTC, so anything else is a rendering choice. Converting a date to a timestamp needs one even more urgently, because without it the input is genuinely ambiguous.
Most converters quietly use the browser's timezone and never say so. That is fine until you are debugging a production log written by a server in UTC while sitting in a UTC-3 office, at which point every timestamp you check is three hours off and you have no indication of why. This page shows UTC and your chosen zone side by side, always, and prints the offset it applied.
Daylight saving turns that ambiguity into something sharper. When clocks go forward, an
hour of wall-clock time never happens; when they go back, an hour happens twice. A date
input landing in either of those windows has no single correct answer, and the honest
behaviour is to say so. This converter detects both cases and labels them, resolving
ambiguous times to the first occurrence — the same convention the upcoming
Temporal API uses.
All of that comes from the browser's own IANA time zone database through
Intl.DateTimeFormat, not from a hand-written offset table. The difference
matters for historical dates: Brazil observed daylight saving until 2019 and abolished it
afterwards, so noon on 15 January 2015 in São Paulo is UTC-2, while noon on the same date
in 2025 is UTC-3. Any tool that stores "Brazil is UTC-3" as a constant is off by an hour
for every summer date before 2019 — and Brazil is far from the worst offender. Rules
change; only the database keeps up.
Store UTC, format at the edge
The practical rule that falls out of all this: keep one instant in one place, in UTC, and
convert to a human timezone only at the moment of display. Store a timestamp or a
timestamptz; never store a local wall clock plus a separate offset column,
and never store a formatted string.
The reason is that offsets expire. If you store "2018-02-17 23:30 with offset -02:00" you have recorded a fact that is true forever, but you have also recorded it in a form that requires you to re-derive the instant on every read. If you store "2018-02-17 23:30, Brazil" you have stored something that changes meaning when the government changes the rules — and Brazil changed them in 2019. A UTC instant never changes meaning. Everything else is a view of it.
The one legitimate exception is future local events. A meeting at 09:00 local time next March genuinely means "whatever 09:00 turns out to be", so the wall clock plus a zone identifier — not an offset — is the correct storage. That is a calendar, not a log.
The year 2038 problem
On 19 January 2038 at 03:14:07 UTC, Unix time reaches 2147483647. That is 231−1, the largest number a signed 32-bit integer can hold. One second later the counter overflows into the sign bit and becomes −2147483648, which renders as 13 December 1901. A clock that has counted forward for 68 years falls 136 years backwards in a single tick.
This is not a theoretical curiosity, and it is not the year 2000 again. Y2K was a
formatting convention; 2038 is arithmetic overflow in a data type. Sixty-four-bit systems
have been fine for years — a 64-bit signed second count runs out around the year 292
billion — but the problem was never really about CPUs. It is about the places where a
32-bit width is frozen into something you cannot easily change: on-disk file formats
(the ext3 inode timestamp), network protocols, the MySQL TIMESTAMP column
type, embedded firmware in meters and controllers and cars with twenty-year service
lives, and every piece of code that ever cast a time to int because it
seemed obviously large enough.
It is already happening in small ways. Anything computing a 20-year expiry date crossed the boundary in 2018; certificate and mortgage systems hit it first. If you enter a timestamp past the limit above, this converter flags it, because "does this still fit in 32 bits?" is a question worth asking of any system you are still going to be maintaining.
Before 1970: negative timestamps
Nothing about the epoch stops the count from going backwards. −1 is 31 December 1969 at 23:59:59 UTC. −2208988800 is 1 January 1900. Negative timestamps are ordinary values, and they turn up constantly in birth dates, historical archives, genealogy and any dataset with a pre-1970 range.
They are also where a lot of tooling quietly falls apart: converters that reject the minus sign, that render the absolute value as a date in 1971, or that lose an hour because a timezone offset was applied in the wrong direction. Older systems used an unsigned 32-bit second count and simply could not express anything before 1970 at all. This converter treats negative input as ordinary input and marks it as pre-epoch so that a stray minus sign is visible rather than silently plausible.
Leap seconds, and why Unix time ignores them
The Earth's rotation is slightly irregular, so UTC is occasionally corrected by inserting a leap second — a real 61-second minute, 23:59:60, added 27 times since 1972. Unix time does not represent them. It defines a day as exactly 86400 seconds, always.
That means Unix time is not a true count of elapsed seconds; it is roughly 27 seconds behind the physical count and drifting. The trade is deliberate and, on balance, correct: because every day is the same length, converting between a timestamp and a calendar date is pure division with no leap-second table to consult, no table to keep updated, and no ambiguity about which version of the table a given machine has. Systems that need real elapsed time use TAI or a monotonic clock instead.
What operating systems do at the moment of insertion varies: some repeat the same Unix value for two consecutive seconds, and large cloud providers "smear" the extra second across several hours so that no clock ever moves backwards. Both approaches mean that during a leap second a Unix timestamp is not a unique instant — a corner case that has caused real outages, and a good argument against using timestamps as unique identifiers.
How this converter works, and what it will not do
Everything runs in your browser. There is no request, nothing is uploaded, and nothing is
stored. The arithmetic lives in a single module with no access to the page, so it can be
tested outside a browser; the timezone rules come from Intl.DateTimeFormat,
which carries the full historical IANA database your browser already ships.
- Range. JavaScript dates cover roughly ±273,000 years around the epoch. Outside that window the converter reports an error instead of guessing.
- Precision. Microseconds are accepted and displayed, but the underlying instant is stored in milliseconds, so sub-millisecond digits are truncated rather than preserved. Nanosecond precision needs BigInt arithmetic, which this tool does not do.
- Historical offsets. Offsets with seconds — São Paulo was UTC-03:06:28 before 1914, under local mean time — are rounded to the minute, because no common date format can express the seconds anyway.
- Leap seconds. Not represented, exactly as Unix time itself does not represent them.
Frequently asked questions
Is 1700000000 in seconds or in milliseconds?
Seconds. It is 14 November 2023, 22:13:20 UTC. The millisecond version of the same instant is 1700000000000, with three more digits. The reliable test is length: a present-day timestamp is 10 digits in seconds, 13 in milliseconds and 16 in microseconds. This converter reads the magnitude and tells you which unit it assumed, and you can override it if the guess is wrong.
Why does the same date give me two different timestamps?
Because a date and time without a timezone is not an instant. 14 November 2023 at 19:13 is 22:13 UTC if you meant São Paulo, and 18:13 UTC if you meant London — a three-hour gap between two timestamps that came from the same typed characters. That is why the timezone selector on this page applies to both directions, and why the result always states the offset it used.
Can a Unix timestamp be negative?
Yes. Negative values are instants before 1 January 1970, one second per unit going backwards: -1 is 31 December 1969 at 23:59:59 UTC, and -806976000 is D-Day, 6 June 1944. Negative timestamps are valid and common in birth dates and historical records, and a surprising number of converters either reject them or silently render the absolute value. This one handles them.
What happens on 19 January 2038?
At 03:14:07 UTC the Unix second count reaches 2147483647, the largest value a signed 32-bit integer can hold. One second later it overflows and wraps to -2147483648, which reads as 13 December 1901. Any system still storing time in a 32-bit signed integer will jump 136 years into the past. Modern 64-bit systems are fine; embedded firmware, old file formats and some database columns are not.
Does Unix time count leap seconds?
No, and that is deliberate. Unix time defines every day as exactly 86400 seconds, so when a leap second is inserted into UTC the same Unix value is used twice or the clock is smeared over several hours. The benefit is that converting a timestamp to a date is pure arithmetic with no lookup table. The cost is that Unix time is not a true count of elapsed SI seconds — since 1972 it is about 27 seconds short.