← All guides

Unix timestamps and time zones, demystified

Somewhere in a log file near you sits a number like 1752192000, holding a precise moment in time and telling you absolutely nothing. Machine time is wonderfully unambiguous and thoroughly unreadable; human time is readable and a minefield of zones, offsets and daylight saving. This guide covers the whole bridge between them: what that number means (midnight UTC, July 11, 2025), the seconds-versus-milliseconds trap, why sensible systems store UTC and render local, what a time zone actually is, and why adding 24 hours doesn't always land you at the same time tomorrow.

What a Unix timestamp actually is

A Unix timestamp is a count of seconds since one agreed-upon instant: January 1, 1970, 00:00:00 UTC — “the epoch.” That's the entire idea. 0 is the epoch itself, 86400 is exactly one day later, and 1752192000 is 2025-07-11T00:00:00 UTC.

The crucial property is that a timestamp is zone-free. It doesn't mean midnight in New York or midnight in Tokyo; it names one instant, the same everywhere on Earth. Two computers on opposite sides of the planet holding 1752192000 agree completely — only their rendering into local wall-clock time differs. That's what makes timestamps ideal for storing, sorting and subtracting.

A few waypoints give the numbers a feel: Unix time crossed 1,000,000,000 on September 9, 2001, and every timestamp since has had 10 digits — which will stay true until November 20, 2286. One honest footnote: Unix time pretends leap seconds don't exist, treating every day as exactly 86,400 seconds. Unless you do astronomy or GPS work, this never matters; for application work it's a feature.

Seconds or milliseconds: count the digits

The number-one timestamp confusion isn't zones — it's units. Some systems count seconds, others count milliseconds, and a raw number doesn't announce which it is. The tell is length: a current timestamp in seconds has 10 digits; in milliseconds, 13.

JavaScript is the milliseconds camp — Date.now() returns 13 digits, and so does Java's System.currentTimeMillis(). The Unix shell's date +%s and Python's time.time() count seconds. Mix them up and the failure is theatrical rather than subtle: interpret a milliseconds value as seconds and you get a date around the year 57,000; interpret seconds as milliseconds and everything happened in January 1970 — 1752192000 read as milliseconds is January 21, 1970. If a “recent” timestamp renders as either of those, your data is fine; your units are wrong. You'll also occasionally meet 16 digits (microseconds) and 19 (nanoseconds) in database and tracing output — same idea, more zeros.

Decode one now: paste any number into the timestamp converter — it auto-detects seconds vs milliseconds, shows the moment in UTC and your local time, and displays the current epoch live. Runs entirely in your browser.

Store UTC, render local

A local time written down without its zone is a riddle: 2026-03-08 02:30 — in which city? Was daylight saving in effect? (In most of the US, that particular minute didn't even happen.) This is why well-behaved systems follow one pattern: store UTC, convert at the edges. Databases and logs hold timestamps or UTC datetimes; the user's zone is applied only at display time. Sorting, comparing and subtracting all work, and two users in different countries see the same instant, each on their own clock.

The honest exception — and it's a real one — is future events that humans scheduled in local terms. A meeting at 9 a.m. in New York next March should be stored as wall time plus the zone name (America/New_York), not as a precomputed UTC instant, because the UTC moment that “9 a.m. in New York” maps to depends on rules that can change between now and then — and governments do change them. The rule of thumb: things that happened get UTC; things humans plan keep their wall time and zone.

A time zone is not an offset

An offset is just a number: UTC+02:00, UTC−05:00. A time zone is a named ruleset: America/New_York means UTC−05:00 in winter, UTC−04:00 in summer, plus the exact dates those switches happen, plus every historical change on record. Those rules live in the IANA time zone database, which operating systems and browsers ship and update as countries fiddle with their clocks.

Mistaking one for the other causes real bugs: an offset captured in January is wrong by July for any zone that observes daylight saving. Offsets also aren't all whole hours — India runs at +05:30 and Nepal at +05:45. And three-letter abbreviations are a trap of their own: CST plausibly means US Central, China Standard or Cuba Standard time. When you need to communicate a zone, use the IANA name; when you need to communicate an instant, use UTC or an explicit offset.

Cross-zone math, handled: the time zone converter converts a date and time between named zones using your browser's built-in IANA database, with daylight saving applied automatically — nothing sent anywhere.

Why daylight saving breaks naive math

Twice a year, arithmetic that looks obviously correct quietly isn't. In a DST-observing zone, the spring-forward day is 23 hours long and the fall-back day is 25 — so “this time tomorrow” is not always “now plus 86,400 seconds.” Add 24 hours to 9 a.m. on the right Saturday and you land on 10 a.m. or 8 a.m. Sunday, depending on the season.

The switch nights are stranger still. On spring-forward night, 2:30 a.m. simply never occurs — clocks jump from 2:00 to 3:00 — and any job scheduled for it doesn't run. On fall-back night, 1:30 a.m. happens twice, and a naive scheduler runs the job both times. This is why seasoned operators schedule critical automation in UTC, or at hours the switch can't touch, and why date libraries distinguish “add one day” (same wall time tomorrow) from “add 24 hours” (same number of elapsed seconds). Those are different operations that agree 363 days a year — do calendar math with a zone-aware library, or do pure arithmetic in UTC, and never hand-roll the hybrid.

ISO 8601: how to write time down

When a moment has to travel as text, the interchange format both humans and machines agree on is ISO 8601: 2026-07-11T09:30:00Z for UTC (the Z means zero offset), or 2026-07-11T09:30:00-04:00 with an explicit offset. Everything runs biggest-to-smallest — year, month, day, hour, minute, second — which buys a quietly great property: sorting the strings alphabetically sorts them chronologically (for timestamps sharing an offset, which is one more reason to log in UTC). It also ends the 03/04/05 ambiguity — March 4 or April 3? — that regional date formats create. The internet profile of ISO 8601 is specified in RFC 3339; it's the form to use in APIs and logs.

Since JSON has no native date type, APIs send either ISO 8601 strings or raw epoch numbers — both live in the wild, so you'll be converting between them for the rest of your career. JWTs, for instance, carry their expiry (exp) and issue time (iat) as epoch seconds; paste a token into the JWT decoder and it turns them into readable dates, or see our JWT guide for the full tour.

The year-2038 problem, in one paragraph

Classic Unix systems stored timestamps in a signed 32-bit integer, which tops out at 2,147,483,647 — an instant that arrives at 03:14:07 UTC on January 19, 2038. One second later, the counter overflows and wraps to 1901. Modern 64-bit systems are safe for roughly 292 billion years, and mainstream operating systems have moved on, but 32-bit timestamps still lurk in embedded devices, old file formats, database columns and network protocols with fixed-width fields. If you maintain anything long-lived that stores time in 32 bits, 2038 is a real deadline — just one with unusually generous notice.

How to read a mystery timestamp

Putting it all together — when a bare number turns up in a log, a database or an API response:

  1. Count the digits. 10 means seconds, 13 milliseconds, 16 microseconds, 19 nanoseconds. (A 9-digit value is a pre-2001 date in seconds.)
  2. Convert it with the timestamp converter and read the result in UTC and local side by side.
  3. Sanity-check the date. January 1970 or the year 57,000 means the unit was wrong, not the data.
  4. Ask what zone the display used. The number itself is UTC-anchored; confusion nearly always comes from a viewer silently rendering it in local time. A result that's off by exactly your UTC offset is the giveaway.
  5. Cross-check a known event — a deploy, an error alert — to confirm the story the timestamp tells.

That five-step routine resolves nearly every “what time did this actually happen” mystery. The deeper habits are two: keep machine time in UTC, and be explicit — about units, offsets and zone names — every time a moment crosses a boundary between systems or people.

Related: JWTs explained: what's inside a token and how to debug it · JSON vs YAML vs CSV: choosing a data format · Why Toolkit runs entirely in your browser