</>
Back to Blog
Time 2026-06-05 7 min read

Unix Timestamps, Properly Explained

The Unix epoch is 1970-01-01 00:00:00 UTC. There is nothing astronomically, culturally, or numerically significant about that date. The original Unix designers picked the start of the year their system shipped, because that was easier than picking anything else. Fifty-five years later, that arbitrary choice is the universal coordinate origin for digital time.

Unix TimestampEpochISO 8601Y2K38Timezones

What it actually is

A Unix timestamp is a count of seconds since the epoch, ignoring leap seconds. That last part matters more than people expect — we'll come back to it.

The number itself has no inherent unit. It's an integer. Whether that integer represents seconds, milliseconds, microseconds, or nanoseconds is a convention you have to know out-of-band. There is no metadata, no marker, no schema. The convention varies by language and ecosystem:

  • Seconds (10 digits today): C time_t, Go time.Time.Unix(), PHP time(), Python time.time() (returns float seconds), most UNIX command-line tools.
  • Milliseconds (13 digits today): JavaScript Date.now(), Java System.currentTimeMillis(), Kotlin / Android, most JSON APIs that emit numbers.
  • Microseconds (16 digits): PostgreSQL epoch for timestamptz, some C++ APIs, profiling output.
  • Nanoseconds (19 digits): Go time.Time.UnixNano(), recent kernel APIs.

The two-cents trick: a 10-digit timestamp lands somewhere between 2001 and 2286. A 13-digit one is in milliseconds. Anything longer is microseconds or nanoseconds. If a parsing layer treats one as the other, you get values 1,000× off — usually showing up as dates in the year 50000, or in 1970.

How to read a stranger's timestamp

If you're handed an opaque integer:

  1. Count digits. 10 → seconds, 13 → ms, 16 → μs, 19 → ns. (Until 2286 this rule holds.)
  2. Convert to UTC and look at the year. If it's wildly wrong, you got the unit wrong.
  3. If it's a float, the integer part is seconds and the fractional part is sub-second precision. Don't round; preserve.

Don't trust the variable name. You will see timestamp_ms holding seconds, created_at_seconds holding milliseconds, and unix_time holding either.

Y2K38

time_t was historically a signed 32-bit integer on Unix. Signed 32-bit max is 2_147_483_647, which corresponds to 2038-01-19 03:14:07 UTC. At 03:14:08, time_t overflows to negative and points back to 1901.

Almost every desktop and server system has moved to 64-bit time_t. The systems that haven't are exactly the ones nobody wants to touch — embedded firmware in industrial equipment, ATMs, sensors, point-of-sale terminals. Y2K38 will not be an apocalypse on the scale of Y2K, but it will produce a steady drip of mysterious failures starting around 2035 as software written today begins to look 13 years ahead.

If you're storing timestamps for the long haul, use 64-bit integers or int64. If you're using a database column type, prefer BIGINT over INT for any field that might hold a millisecond timestamp — INT overflows in 1970 + (2³¹ − 1)ms ≈ year 1995 already.

Leap seconds: the lie

Unix time pretends every day has exactly 86,400 seconds. The Earth disagrees by about 1 second per 18 months. The international time authorities periodically insert a "leap second" — 23:59:60 UTC — to keep atomic time aligned with mean solar time.

Unix time can't represent 23:59:60. The convention is that the leap second either gets skipped (the Unix clock pauses) or replays the previous second (the clock smears). Most NTP-synchronized servers smear: spread the leap second across 24 hours, so each second is imperceptibly longer. Google publicized its smearing algorithm in 2008; it's now a de-facto standard.

This matters in three places:

  • Financial systems that need millisecond-level ordering during the leap second window.
  • Anything storing intervals. A 10-second interval across a leap second is 11 actual seconds.
  • Logs and metrics — duplicate timestamps on the second boundary, or apparent gaps.

The IERS announced in 2022 that leap seconds are scheduled to be abandoned by 2035. They will still happen, infrequently, until then.

Timezones are not part of the timestamp

This trips up everyone. A Unix timestamp has no timezone. It's a count of seconds since a UTC moment. The timezone enters the picture only when you convert to a wall-clock representation like "2026-06-05 14:30."

What this means in practice:

  • Storing a wall-clock string like "2026-06-05 14:30" without a timezone is data corruption. You don't know what moment it refers to.
  • Storing a Unix timestamp loses no information about when, only about where the user was when it happened. If your business cares where, store the timezone separately as IANA name (Asia/Shanghai, America/New_York), not as an offset (+08:00) — offsets don't capture DST rules.
  • ISO 8601 strings with a Z or offset suffix (2026-06-05T06:30:00Z) are unambiguous. Without the suffix, they're ambiguous and you should reject them.

DST: the recurring bug

Daylight Saving Time means certain wall-clock times don't exist (the spring-forward gap) and others happen twice (the fall-back overlap).

In US Eastern time, on the second Sunday of March, 2:00 to 3:00 doesn't exist — the clock jumps from 1:59:59 to 3:00:00. A meeting scheduled for "2:30 every Tuesday" in someone's calendar — what does it mean on that Sunday? (It usually means 3:30, because most calendar systems quietly skip the missing hour.)

On the first Sunday of November, 1:00 to 2:00 happens twice. A wall-clock value of 01:30 on that day is ambiguous — it refers to two different Unix timestamps, exactly 3,600 seconds apart.

Tools that get this wrong run cron jobs zero times (in the gap) or twice (in the overlap). Don't store wall-clock + offset pairs across DST transitions. Store the timestamp.

Monotonic time vs wall-clock time

System time can move backwards. The clock can be manually changed. NTP can step the clock forward or back to correct drift. A laptop sleeping for an hour wakes up with a different idea of "now."

If you're measuring an interval — request latency, time since a button was pressed, animation frame timing — use monotonic time, not wall-clock. Monotonic clocks count seconds from an arbitrary system-defined origin (often boot) and never go backwards. The names vary: clock_gettime(CLOCK_MONOTONIC) in C, process.hrtime() in Node, time.monotonic() in Python, Instant in Rust.

If you measure with Date.now() and the system NTP-corrects mid-measurement, your interval can be negative. Worse, it can be very large positive when a manual clock change moved the clock forward. Bugs filed about this are universally "the clock did something weird"; the answer is universally "use monotonic."

Common pitfalls

  • Conflating seconds and milliseconds. The 1000× error is the most common timestamp bug in the world.
  • Storing local time without timezone. Eventually someone will read your data from a different machine.
  • Using offset strings (+08:00) for storage. They don't capture DST rules; the city's offset on June 1st may not be the same as on December 1st.
  • Measuring intervals with wall-clock time. Use monotonic.
  • Rounding. A timestamp rounded to seconds and back is not the original — you lose up to 999ms of information. Keep precision until display.
  • Comparing timestamps from different precision sources without normalizing. Date.now() > some_seconds_field is always true.

Practical rules

  • Store UTC. Convert to local time only at the display edge.
  • Use 64-bit integers for new code. BIGINT for the database.
  • Pick one precision per system (seconds or milliseconds) and document it. Don't mix.
  • For human-perceived intervals, monotonic clock. For points in absolute time, wall clock.
  • Store timezones as IANA names, not offsets, when "where" matters.
  • Reject wall-clock strings that don't carry timezone information.

Convert and inspect

The timestamp tool on this site converts seconds, milliseconds, and microseconds against any timezone, and flags ambiguous cases (DST repeats, second/millisecond confusion). All client-side.

Open the timestamp tool

Related guides

Keep the session useful with adjacent reading instead of exiting after one article.

View all guides

Cookie Consent

We use cookies to enhance your experience and show relevant ads. You can customize your preferences.