X Xerobit

Epoch Time Explained — What Is Unix Epoch and Why Does It Start January 1, 1970?

Unix epoch time counts seconds from January 1, 1970 00:00:00 UTC. Learn why 1970 was chosen, how to work with epoch time in JavaScript and Python, the Year 2038 problem, and...

Mian Ali Khalid · · 4 min read
Use the tool
Timestamp Converter
Convert Unix timestamps, epoch seconds/milliseconds, and ISO 8601 dates.
Open Timestamp Converter →

Unix epoch time is the number of seconds elapsed since January 1, 1970, 00:00:00 Coordinated Universal Time (UTC). It’s the universal language for timestamps in computer systems.

Convert epoch timestamps instantly with the Timestamp Converter.

Why January 1, 1970?

The Unix operating system was developed at Bell Labs in the late 1960s. When the developers needed a reference point for timestamps, they chose a date “in the recent past” — recent enough to not require large numbers for common dates.

The date went through several iterations:

  • January 1, 1971 (first version)
  • January 1, 1970 (settled on around 1973 when Unix was being more formally defined)

It’s arbitrary, but once chosen, every Unix-like system adopted it. Today it’s the universal reference point for computing time.

What the epoch number looks like

January 1, 1970 00:00:00 UTC = 0
January 1, 2000 00:00:00 UTC = 946684800
January 1, 2026 00:00:00 UTC = 1767225600
May 12, 2026    14:30:00 UTC ≈ 1747063800

The number grows by 1 every second. By 2026, we’re around 1.7 billion seconds past the epoch.

Seconds vs milliseconds

Different systems use different granularities:

// Seconds (Unix standard):
Math.floor(Date.now() / 1000)  // ~1747063800

// Milliseconds (JavaScript standard):
Date.now()                     // ~1747063800000

// Common confusion: JavaScript always uses milliseconds
new Date(1747063800)           // 1970-01-21... (treating as ms, not seconds!)
new Date(1747063800 * 1000)    // 2026-05-12... (correct: multiply by 1000)
new Date(1747063800000)        // 2026-05-12... (correct: already ms)

Rule of thumb: If the number has 10 digits, it’s seconds. If it has 13 digits, it’s milliseconds.

Getting epoch time

// JavaScript:
const seconds = Math.floor(Date.now() / 1000);     // 10-digit number
const milliseconds = Date.now();                     // 13-digit number
const performance = performance.now();               // High-precision ms since page load
import time
from datetime import datetime, timezone

# Seconds (float):
time.time()                                          # 1747063800.123456

# Seconds (integer):
int(time.time())                                     # 1747063800

# Milliseconds:
int(time.time() * 1000)                             # 1747063800123

# Using datetime:
datetime.now(timezone.utc).timestamp()               # 1747063800.123456
# Shell:
date +%s         # seconds: 1747063800
date +%s%N       # nanoseconds: 1747063800123456789

Converting epoch to human-readable

// Milliseconds to Date:
new Date(1747063800000).toISOString();
// "2026-05-12T14:30:00.000Z"

// Seconds to Date:
new Date(1747063800 * 1000).toISOString();
// "2026-05-12T14:30:00.000Z"
from datetime import datetime, timezone

# Seconds to datetime:
datetime.fromtimestamp(1747063800, tz=timezone.utc)
# datetime(2026, 5, 12, 14, 30, 0, tzinfo=timezone.utc)

# Format:
datetime.fromtimestamp(1747063800, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
# "2026-05-12 14:30:00 UTC"

The Year 2038 Problem

32-bit signed integers max out at 2,147,483,647 — which corresponds to January 19, 2038, 03:14:07 UTC.

After that point, a signed 32-bit integer wraps to a large negative number, causing timestamp overflow.

// C: 32-bit time_t overflow
time_t t = 2147483647;  // 2038-01-19 03:14:07 UTC
t++;                    // -2147483648 = December 13, 1901 (!)

Solutions:

  • Modern 64-bit systems use 64-bit time_t — can represent dates up to year 292 billion
  • MySQL’s TIMESTAMP type (32-bit) has a 2038 problem; DATETIME (64-bit) does not
  • Most modern software already uses 64-bit timestamps

JavaScript is safe: JavaScript uses 64-bit floats for Date, handling dates up to year 275,760.

Negative epoch values

// Dates before January 1, 1970 have negative epoch values:
new Date(-1000).toISOString();  // "1969-12-31T23:59:59.000Z"
new Date(-86400000).toISOString();  // "1969-12-31T00:00:00.000Z"
datetime.fromtimestamp(-1000, tz=timezone.utc)
# datetime(1969, 12, 31, 23, 43, 20, tzinfo=timezone.utc)

Epoch time in databases

-- PostgreSQL: store as BIGINT (milliseconds) or TIMESTAMPTZ
-- TIMESTAMPTZ is stored as UTC microseconds since epoch internally

-- MySQL: TIMESTAMP (32-bit, expires 2038) vs DATETIME (64-bit, recommended)
-- Use BIGINT UNSIGNED for millisecond epoch if you need portability

-- SQLite: store as INTEGER (seconds) for simplicity:
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    created_at INTEGER NOT NULL  -- Unix seconds
);

Related posts

Related tool

Timestamp Converter

Convert Unix timestamps, epoch seconds/milliseconds, and ISO 8601 dates.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.