Skip to content

← All writing

Unix Timestamps Explained: Epoch Time, Time Zones, and the 2038 Problem

· by Andergrove Software

A Unix timestamp is just a count of seconds since 1970-01-01 00:00:00 UTC, the moment programmers call "the epoch." A value like 1751155200 is not random; it is one specific instant, the same instant everywhere on Earth, because it is measured in UTC with no time zone attached. That simplicity is why timestamps are everywhere in logs, databases and APIs, and also where the confusion starts.

Here is what the number means, the seconds-versus-milliseconds trap, why you should store UTC and format only on display, and the Year 2038 problem waiting in older systems. You can convert any timestamp both ways in the timestamp converter as you read.

What the number counts

The epoch is 1970-01-01T00:00:00Z. A Unix timestamp is the number of seconds elapsed since then (ignoring leap seconds). Because it is anchored to UTC, the same timestamp refers to the same instant no matter where the reader sits: a server in Sydney and a browser in London agree on 1751155200, then each formats it into local time for display. Store the instant, present the local time.

Seconds vs. milliseconds: the off-by-1000 bug

The single most common timestamp bug: Unix time is in seconds, but many languages report milliseconds. JavaScript's Date.now() returns milliseconds; most Unix tooling, databases and APIs use seconds. Mix them up and you land in 1970 or somewhere in the year 56000.

A quick sniff test: a seconds timestamp for "now" has 10 digits (until the year 2286); a milliseconds one has 13. If a date comes out wildly wrong, check whether you need to multiply or divide by 1000.

Time zones and daylight saving

A Unix timestamp has no time zone: it is always UTC. Time zones and daylight saving only enter the picture when you format a timestamp for a human. The rule that saves the most pain is simple: store and compute in UTC, and convert to local time only at the edges, on display. Storing local times invites ambiguity (which 01:30 during the autumn rollback?) and breaks arithmetic across DST boundaries.

The Year 2038 problem

Many older systems store the timestamp in a signed 32-bit integer. The largest value it can hold is 2,147,483,647 seconds, reached at 03:14:07 UTC on 19 January 2038. One second later it overflows to a negative number and wraps back to December 1901. It is the 21st-century echo of Y2K, affecting embedded devices, old file formats, and legacy C code that still uses a 32-bit time_t.

The fix is already widespread: 64-bit timestamps, which push the overflow roughly 292 billion years out. Modern operating systems and languages use 64-bit time; the remaining risk lives in embedded and legacy systems that are hard to update.

Get epoch time anywhere

JavaScript      Math.floor(Date.now() / 1000)        // seconds (Date.now() is ms)
Python          import time; int(time.time())
SQL (Postgres)  SELECT extract(epoch from now())::bigint;
Shell           date +%s

Convert it

Paste any epoch value, seconds or milliseconds, into the Andergrove Timestamp Converter to see the human date in UTC and your local zone, or go the other way from a date to a timestamp. It runs entirely in your browser. Counting down to a specific date instead? The age & countdown calculator handles that. Timestamps even hide inside modern identifiers: a UUIDv7 is timestamp-prefixed, so you can generate a sortable, time-ordered ID with the UUID generator.