Unix timestamps are one of the most universally used data types in software — every log line, every database record, every API response includes a timestamp of some kind. Yet they remain a constant source of bugs in production. This guide gives you a complete mental model.
What Is the Unix Epoch?
The Unix epoch is the reference point: January 1, 1970, 00:00:00 UTC. A Unix timestamp is simply the number of seconds (or milliseconds) that have elapsed since that moment. The choice of 1970 was arbitrary — it was the beginning of the Unix operating system's widespread deployment.
// Current timestamps in various languages
JavaScript: Date.now() // milliseconds
JavaScript: Math.floor(Date.now() / 1000) // seconds
Python: import time; time.time() // seconds (float)
Python: int(time.time()) // seconds (int)
Go: time.Now().Unix() // seconds
Go: time.Now().UnixMilli() // milliseconds
SQL: EXTRACT(EPOCH FROM NOW())::bigint // PostgreSQL, seconds
Seconds vs Milliseconds: The Biggest Footgun
The most common timestamp bug is mixing seconds and milliseconds. Seconds timestamps are 10 digits (e.g. 1705318200). Milliseconds timestamps are 13 digits (e.g. 1705318200000). If you pass a milliseconds value to code expecting seconds, dates will appear thousands of years in the future.
Date.now() returns milliseconds. Most Unix tools and databases expect seconds. Always divide by 1000 when passing JS timestamps to Unix-native systems.ISO 8601 — The Right String Format
When you must store or transmit a timestamp as a string (JSON APIs, logs, HTML), always use ISO 8601:
2025-07-20T14:30:00Z // UTC, recommended
2025-07-20T14:30:00+05:30 // with offset
2025-07-20T14:30:00.123Z // with milliseconds
The trailing Z denotes UTC (zero offset). Always include timezone information — a timestamp without a timezone is ambiguous and dangerous.
Timezone Pitfalls
Unix timestamps themselves are always UTC — there is no timezone embedded in a timestamp. The timezone only matters when converting to a human-readable display. The rule is: store in UTC, display in local.
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Store local time in DB | Ambiguous during DST transitions | Store UTC Unix timestamps |
| Parse date without timezone | Interpreted as local time, wrong in UTC | Always append Z or offset |
| Compare timestamps cross-timezone | String comparison gives wrong order | Convert to Unix int, then compare |
| Use Date string in SQL | Server timezone affects interpretation | Use TIMESTAMP WITH TIME ZONE |
The Year 2038 Problem
32-bit signed integers overflow at 2147483647 — January 19, 2038 at 03:14:07 UTC. Systems still using 32-bit timestamps will roll over to negative values, behaving as if it's January 1, 1901. Modern 64-bit systems are immune (they safely represent dates 292 billion years into the future), but embedded systems and legacy code are at risk.
Date Arithmetic
Timestamp arithmetic is simple when you think in seconds:
// Add 30 days to a timestamp
var thirtyDaysLater = timestamp + (30 * 24 * 60 * 60); // seconds
// or
var thirtyDaysLater = timestamp + (30 * 24 * 60 * 60 * 1000); // ms
// Check if a token has expired
var isExpired = Date.now() / 1000 > exp_claim; // JWT exp is in seconds
// Get days between two timestamps
var days = Math.abs(ts2 - ts1) / 86400;