Every log line, database row, JWT token, and cron job you've ever touched quietly runs on one number: the count of seconds since midnight UTC, January 1, 1970. Here's how that number works, why it exists, the seconds-vs-milliseconds trap, and the 2038 deadline hiding inside 32-bit systems.
Calendars are a mess for computers. Timezones shift, months have random lengths, leap years have rules with exceptions, and "March 9" doesn't sort usefully next to "March 10, 2024, 3 p.m. EST." A single monotonic integer fixes all of it: subtract two timestamps and you have the elapsed seconds; sort them and you have chronology; format them at display time and only then care about timezones.
When the early Unix developers needed a zero point for that counter, they picked 00:00:00 UTC on January 1, 1970 — not for any astronomical reason, but because it was a tidy boundary when time_t was designed in the early 1970s. That arbitrary choice became one of computing's most durable standards. Unix time now underlies timestamps in Linux and macOS file systems, most databases, HTTP headers via conversion, JWT claims like exp and iat, and every programming language's date library at some level.
One day is exactly 86,400 seconds — POSIX says so, leap seconds notwithstanding. So the arithmetic is honest grade-school math: 1735689600 ÷ 86,400 = 20,089 days, and 20,089 days past January 1, 1970 is January 1, 2025. Some anchors worth internalizing:
| Timestamp (s) | UTC moment | Anchor for |
|---|---|---|
| 0 | 1970-01-01 00:00:00 | The epoch itself |
| 86,400 | 1970-01-02 00:00:00 | One day = 86,400 s |
| 1,000,000,000 | 2001-09-09 01:46:40 | The "billennium" |
| 1,700,000,000 | 2023-11-14 22:13:20 | Recent round number |
| 1,800,000,000 | 2027-01-15 08:00:00 | Next billion milestone |
| 2,147,483,647 | 2038-01-19 03:14:07 | 32-bit signed max |
Negative numbers are legal too: -1 is 23:59:59 on December 31, 1969, and tools that reject negative timestamps are enforcing policy, not math. And days-of-week come free: because 20,089 mod 7 lands where it lands, the epoch itself was a Thursday, which makes any timestamp's weekday computable with one modulo.
Paste an epoch in seconds or milliseconds, get UTC, local time, ISO 8601, and relative time — or convert a date back to epoch.
Epoch Converter →Unix time proper is seconds. JavaScript, having shipped with a millisecond-resolution Date object in 1995, returns milliseconds forever now — so Date.now() gives a 13-digit number while date +%s in your shell gives 10 digits. The bug pattern: multiply when you meant to divide, and a date of "January 1970" shows up for something obviously recent, or a far-future date (the year 55,000-something) for something that should have expired.
The digit-count check catches it instantly: 10 digits is seconds, 13 digits is milliseconds (right now; seconds will hit 11 digits in 2286, so the heuristic has runway). Java's System.currentTimeMillis() and Go's time.Now().UnixMilli() are milliseconds; Go's .Unix(), Python's time.time() (fractional seconds aside), and SQL's UNIX_TIMESTAMP() are seconds. Always check which one an API contract means before doing arithmetic.
A signed 32-bit integer tops out at 2,147,483,647. As a Unix timestamp, that's 03:14:07 UTC on January 19, 2038 — and one second later, a 32-bit counter wraps to its negative extreme, which reads as December 13, 1901. File dates, embedded controllers, transaction logs, and anything else still storing time in 32 bits will lurch backward by 68 years.
The fix is boring and mostly done: use 64-bit time_t, which mainstream Linux, macOS, and Windows have for years, and which doesn't overflow for 292 billion years. The residual risk hides in embedded firmware, industrial controllers, and old compiled binaries with nobody left who remembers deploying them. It's Y2K's sequel with a smaller blast radius and, so far, even less publicity — 2038 is fourteen years out as of this writing, and the migration is quiet but real.
t0 and compare against it, rather than two independent clock reads; system clocks slew under NTP.exp and iat claims in a JWT are epoch seconds — the most common place web developers meet raw timestamps, and the most common place the seconds/milliseconds confusion bites.It's the number of seconds elapsed since 00:00:00 UTC on January 1, 1970, ignoring leap seconds. Timestamp 0 is that exact moment; 1735689600 is 2025-01-01 00:00:00 UTC; anything before 1970 is negative. Because it's a plain number, computers can store, sort, and compare dates with integer math instead of calendars.
The Unix operating system's early developers picked January 1, 1970 as a convenient round starting point for the system clock, and it stuck. The date itself has no astronomical or historical significance — it was just midnight on New Year's Day of a year that was already a convenient round number when time_t was designed in the early 1970s.
Count digits. Today's epoch-seconds timestamps are 10 digits (e.g., 1750000000); JavaScript-style milliseconds are 13 digits (1750000000000). A 13-digit number is exactly 1,000× the seconds value. Nine-digit numbers around 10^9 are seconds from the early 2000s; 12 digits are pre-2001 milliseconds.
Officially, Unix time ignores them: every day is exactly 86,400 seconds, and leap seconds are handled by slewing or stepping the system clock around the insertion. POSIX defines it this way, which means Unix time drifts up to ~27 seconds from true UTC that NTP constantly corrects. For almost all application code, you can ignore leap seconds entirely.
That's 03:14:07 UTC on January 19, 2038 — the maximum value of a signed 32-bit integer, so systems still storing time_t in 32 bits wrap around to negative and read December 1901. It's the Y2038 problem. Mainstream 64-bit Linux, macOS, and Windows are already unaffected; the risk lives in embedded devices, old databases, and firmware nobody remembers deploying.