Unix Timestamps: The Developer's Complete Reference
Every developer works with timestamps, but few pause to think about what that integer actually represents. This guide covers everything from the basics of epoch time to the edge cases that cause production bugs at 3 AM.
What is a Unix timestamp?
A Unix timestamp (also called epoch time or POSIX time) is the number of seconds that have elapsed since January 1, 1970, 00:00:00 UTC. That moment is called the Unix epoch.
Right now, the timestamp is a 10-digit number somewhere above 1.7 billion. By convention, the epoch is timezone-agnostic: it always refers to a specific moment in UTC. The same timestamp maps to different wall-clock times depending on the observer's timezone, but the underlying value is universal.
// Current Unix timestamp in JavaScript
Math.floor(Date.now() / 1000); // seconds
Date.now(); // milliseconds
Seconds vs milliseconds vs microseconds
Different systems use different precisions:
| Precision | Digits | Example | Used by |
|---|---|---|---|
| Seconds | 10 | 1713225600 |
Unix, PHP, Python, Go, MySQL |
| Milliseconds | 13 | 1713225600000 |
JavaScript, Java, Dart |
| Microseconds | 16 | 1713225600000000 |
PostgreSQL, Rust, C |
| Nanoseconds | 19 | 1713225600000000000 |
Go time.UnixNano() |
The most common source of timestamp bugs is mixing up seconds and milliseconds. If you pass a seconds-based timestamp to a function expecting milliseconds, you get a date in January 1970. If you do the reverse, you get a date thousands of years in the future.
A quick heuristic: if the number has 10 digits, it is seconds. If it has 13, milliseconds. This works reliably for dates between 2001 and 2286.
The Y2038 problem
On January 19, 2038 at 03:14:07 UTC, a 32-bit signed integer holding a Unix timestamp will overflow. The value 2147483647 (2^31 - 1) wraps around to -2147483648, which maps to December 13, 1901.
This is a real concern for embedded systems, IoT devices, and legacy databases that still use 32-bit integers for timestamps. Modern 64-bit systems are unaffected -- a 64-bit signed integer can represent dates up to about 292 billion years from now.
If you are designing a schema today, always use a 64-bit integer or a proper TIMESTAMP / DATETIME column type. Never use INT(4) for timestamps.
Timezone handling
Timestamps themselves are timezone-free. The bugs come from converting between timestamps and human-readable dates.
Common mistakes
Storing local time without timezone info. If your database has 2026-04-04 15:00:00 without a timezone, you cannot reliably convert it back to a timestamp. Is that 15:00 in UTC? Eastern? Cairo? Always store either a UTC timestamp or a datetime with explicit timezone offset.
Assuming the server timezone is stable. A deploy to a different region, a Docker container with a different TZ environment variable, or a DST transition can silently shift all your conversions.
Ignoring DST transitions. On the day clocks spring forward, there is a gap. On the day they fall back, there is an overlap. A naive "add 86400 seconds for next day" can land you on the wrong date during these transitions.
Best practice
Store timestamps as UTC integers. Convert to local time only at the display layer, using the user's timezone preference.
from datetime import datetime, timezone
# Always work in UTC internally
now_utc = datetime.now(timezone.utc)
timestamp = int(now_utc.timestamp())
# Convert to local only for display
from zoneinfo import ZoneInfo
local = datetime.fromtimestamp(timestamp, tz=ZoneInfo("Africa/Cairo"))
Common conversion patterns
JavaScript
// Timestamp to Date
const date = new Date(1713225600 * 1000); // seconds to ms
// Date to timestamp
const ts = Math.floor(new Date('2026-04-04T12:00:00Z').getTime() / 1000);
// Format for display
date.toLocaleString('en-US', { timeZone: 'America/New_York' });
Python
import time
from datetime import datetime, timezone
# Current timestamp
ts = int(time.time())
# Timestamp to datetime
dt = datetime.fromtimestamp(ts, tz=timezone.utc)
# String to timestamp
dt = datetime.fromisoformat('2026-04-04T12:00:00+00:00')
ts = int(dt.timestamp())
SQL (PostgreSQL)
-- Current timestamp
SELECT EXTRACT(EPOCH FROM NOW())::bigint;
-- Timestamp to readable date
SELECT TO_TIMESTAMP(1713225600) AT TIME ZONE 'UTC';
-- Date to timestamp
SELECT EXTRACT(EPOCH FROM '2026-04-04 12:00:00+00'::timestamptz)::bigint;
Go
import "time"
// Current timestamp
ts := time.Now().Unix()
// Timestamp to Time
t := time.Unix(1713225600, 0).UTC()
// Parse string to timestamp
t, _ := time.Parse(time.RFC3339, "2026-04-04T12:00:00Z")
ts := t.Unix()
Negative timestamps
Dates before the epoch (January 1, 1970) are represented as negative numbers. December 31, 1969 at 23:59:59 UTC is -1. This works correctly in most languages, but some systems reject negative timestamps or treat them as unsigned integers.
If your application needs to handle historical dates before 1970, test your entire stack with negative values. Database columns, API validators, and frontend date pickers can all silently fail.
Leap seconds
Unix time pretends leap seconds do not exist. When a leap second occurs, the timestamp either repeats the previous second or skips ahead, depending on the implementation. In practice, most systems use "smeared" time (Google's approach), spreading the leap second across a longer window.
For the vast majority of applications, this does not matter. If you are building high-frequency trading or satellite systems, you need TAI (International Atomic Time) instead of Unix time.
Timestamp ranges to remember
| Date | Timestamp | Significance |
|---|---|---|
| 1970-01-01 | 0 |
Unix epoch |
| 2001-09-09 | 1000000000 |
First 10-digit timestamp |
| 2009-02-13 | 1234567890 |
Memorable number |
| 2038-01-19 | 2147483647 |
32-bit overflow |
| 2286-11-20 | 9999999999 |
Last 10-digit timestamp |
Debugging timestamp issues
When a date looks wrong, follow this checklist:
- Check the precision. Is the value in seconds, milliseconds, or microseconds? Divide or multiply by 1000 and see if the result makes sense.
- Check the timezone. Convert the timestamp to UTC first. If the UTC date is correct but the local date is wrong, your timezone conversion is the issue.
- Check for string vs number. Some APIs return timestamps as strings. Parsing
"1713225600"as a number works, but concatenating it with another string produces garbage. - Check for signed vs unsigned. If a date shows up as a far-future year, the system might be interpreting a negative timestamp as a large positive number.
ISO 8601 vs Unix timestamps
ISO 8601 strings like 2026-04-04T12:00:00Z are human-readable and self-documenting. Unix timestamps are compact and easy to sort and compare. Use ISO 8601 in APIs and logs where readability matters. Use Unix timestamps for storage, indexing, and computation where performance matters.
Many teams use both: store as integer, serialize as ISO 8601 in API responses.
Try our Timestamp Converter to convert between Unix timestamps and human-readable dates instantly -- right in your browser, with timezone support built in.