Unix Timestamp Converter
Convert epoch timestamps to dates and back, in any timezone — including the two local times that are not one instant.
A timestamp is a count, not a date
The Unix epoch — 1970-01-01T00:00:00Z — is the moment Unix time was
defined to start, and a Unix timestamp is simply the number of units since then. In
seconds, ten digits (for dates after 2001); in milliseconds, thirteen; in
microseconds, sixteen; in nanoseconds, nineteen. The unit matters more than it
sounds like it should, because a converter that cannot tell them apart does not
error on the mismatch — it prints a plausible-looking wrong year. Measured:
reading 1516239022000 (a millisecond value, which is 2018) as seconds
yields +050017. This tool reads the digit count, says which unit it
decided on, and shows what the alternatives would have given. A number is never
silently assumed; it is interpreted with the interpretation on the screen.
Timezones are the point
The value is a point in time; the zone is how it reads on a wall clock. That
separation is where converters break, and it breaks in real, checkable ways. A zone
is not a fixed offset: Asia/Kathmandu moved from +05:30 to
+05:45 in 1986, so any converter offering "UTC+5" as a choice gets every
Nepali timestamp from before then wrong, and cannot represent Chatham or Lord Howe
at all. Offsets are also historical, which is why this page computes the offset for
every instant rather than caching one per zone.
And a wall clock does not always read one instant. Twice a year a local time maps to zero instants (spring forward — the time never happened) or two (fall back — it happened twice). Plenty of converters steer around this or pick one silently. This page treats both as the real cases they are: it says the time never existed and shows where the clock jumped to, or it lists both instants and lets you choose.
Do it without this tool
Four languages, four ways to make the mistake, and one honest note about the shell
split. First, the shell: GNU and BSD date genuinely use different flags,
so it is worth knowing which one you are on.
# GNU coreutils (Linux) date -d @1516239022 -u # Thu Jan 18 01:30:22 UTC 2018 # BSD date (macOS has no GNU date): -r, not -d date -r 1516239022 -u # Thu Jan 18 01:30:22 UTC 2018 # the other direction (both: epoch seconds as of now) date -d "$(date +%s -d '2018-01-18 01:30:22')" -u # GNU date -j -f '%Y-%m-%d %H:%M:%S' '2018-01-18 01:30:22' +%s # BSD
from datetime import datetime, timezone ts = 1516239022 # Naive: format in the machine's local zone. On a UTC+2 host this prints 03:30, # silently applying a zone that is NOT in the timestamp. That's the bug. print(datetime.fromtimestamp(ts)) # 2018-01-18 03:30:22 # Aware: the instant, in UTC. This is the one that's actually 1516239022. print(datetime.fromtimestamp(ts, tz=timezone.utc)) # 2018-01-18 01:30:22+00:00 # round trip: a datetime -> epoch seconds dt = datetime(2018, 1, 18, 1, 30, 22, tzinfo=timezone.utc) print(int(dt.timestamp())) # 1516239022
-- Postgres: to_timestamp returns timestamptz (seconds) SELECT to_timestamp(1516239022); -- 2018-01-18 01:30:22+00 -- MySQL: FROM_UNIXTIME (seconds), a DATETIME in the session zone SELECT FROM_UNIXTIME(1516239022); -- MySQL: the same instant, psql-style. Prefer these for BI/ETL. SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 1516239022 * INTERVAL '1 second'; -- Pg SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + INTERVAL 1516239022 SECOND; -- MySQL
// The Date constructor takes *milliseconds*, not seconds — the * 1000 is the whole job.
new Date(1516239022 * 1000).toISOString(); // '2018-01-18T01:30:22.000Z'
// the other direction (Math.floor: fractional microseconds must not round up)
Math.floor(Date.now() / 1000); // epoch seconds
// a string back to epoch millis (date-only parses as UTC — see the FAQ)
Date.parse('2018-01-18') // 1516060800000
The standards, which is which
Three documents govern the boxes above, and conflating them is where the prose
gets it wrong. The POSIX time_t defines the epoch and
the count: seconds since 1970-01-01T00:00:00Z, with the leap-second rule from the
FAQ. RFC 3339, not "ISO 8601", is the format engineers mean when
they write 2018-01-18T01:30:22Z — it is a profile of ISO 8601 that
requires the T, requires a timezone designator (a Z or an
offset like +02:00), and forbids the comma decimal separator that ISO
8601 allows. The IANA timezone database is what actually knows that
Kathmandu changed offset in 1986: it is a published, versioned dataset that updates
several times a year, and your browser ships a copy of it. That is why this page's
zone list is never hardcoded — it reads Intl.supportedValuesOf('timeZone')
from the platform, so a new zone name appears here the day the browser knows it.
FAQ
Why does 13 digits turn into a date in the year 50017?
A millisecond value read as seconds. 1516239022000 is 2018 in
milliseconds; read as the same unit you'd give a ten-digit number, it's the year
50017. The page infers the unit from the digit count and shows the alternatives.
Is a Unix timestamp always UTC?
Yes. The timestamp is elapsed seconds since the epoch, period — the zone only ever
changes how it is displayed, never the number. Switch the zone control above and the
epoch rows stay put. It is the same count wherever it turns up: a JWT's
exp, nbf and iat are these seconds too, so
“why does my token say expired” is answered by pasting the number here
or by reading the whole thing on
the jwt-debugger page, which does the same arithmetic.
What actually happens in 2038?
The signed 32-bit time_t overflows at 2147483647: 2038-01-19T03:14:07Z.
A naive 32-bit system wraps to 1901 the next second; 64-bit systems are unaffected.
This page names the boundary when a value is near it.
Does Unix time count leap seconds?
No. Unix time skips leap seconds, so a wall clock showing 23:59:60 has no corresponding timestamp, and this page rejects it with that explanation rather than rolling it forward.
What does a local time that "happens twice" mean — and which one did my database store?
The clock repeated an hour. Both instants are rendered as rows here; which one your database stored depends on whether it read the clock before or after the transition. Store UTC on write and format on read.
Why do new Date("2018-01-18") and new Date("2018-01-18T00:00") differ?
The ES spec reads date-only strings as UTC and date-time strings without an offset as local. Two strings differing only by a time component can land hours apart. This page states which rule it applied instead of picking silently.