Unix Timestamps: What Every Developer Needs to Know
A Unix timestamp is the number of seconds elapsed since 00:00:00 UTC on 1 January 1970 โ the Unix epoch. It's a single integer that represents any point in time unambiguously, with no timezone information needed, because it's always measured from UTC.
Convert timestamps instantly: Unix timestamps to human-readable dates and back โ with local and UTC display and a live ticking current timestamp.
Open Timestamp Converter โTable of Contents
Why Timestamps Beat Datetime Strings
Storing dates as strings like "2026-02-24 09:30:00" creates real problems. Which timezone? Are they comparable across timezones? Does your database interpret them consistently? Unix timestamps sidestep all of these โ they're timezone-agnostic integers that sort correctly, compare correctly, and mean the same thing on every server in every country.
Seconds vs Milliseconds
Unix timestamps come in two flavours: seconds (Unix/Linux systems, most server-side languages) or milliseconds (JavaScript's Date.now(), Java's System.currentTimeMillis()). The easiest detection: 10-digit timestamps are seconds, 13-digit are milliseconds. Forget to multiply by 1000 in JavaScript and your date lands in 1970.
// JavaScript
const ts = 1709488000; // seconds from server
const correct = new Date(ts * 1000); // โ
const wrong = new Date(ts); // โ shows 1970
// Python
from datetime import datetime, timezone
dt = datetime.fromtimestamp(1709488000, tz=timezone.utc)
ts = int(dt.timestamp())
-- PostgreSQL
SELECT to_timestamp(1709488000) AT TIME ZONE 'UTC';
-- MySQL
SELECT FROM_UNIXTIME(1709488000);
Always Store in UTC
Store timestamps in UTC. Display them in the user's local timezone at render time. Never store local time โ DST transitions, server timezone changes, and users in different locations will all produce bugs that are incredibly difficult to reproduce.
Duration Arithmetic
// Duration between two events
const durationSeconds = endTs - startTs;
const durationHours = durationSeconds / 3600;
// "Time ago" calculation
const secondsAgo = Math.floor(Date.now() / 1000) - timestamp;
if (secondsAgo < 60) return `${secondsAgo}s ago`;
if (secondsAgo < 3600) return `${Math.floor(secondsAgo / 60)}m ago`;
The Year 2038 Problem
32-bit signed integers overflow at 2,147,483,647 โ corresponding to 03:14:07 UTC on 19 January 2038. Most modern systems use 64-bit integers, which are safe for ~292 billion years. If you're working with embedded systems or legacy code, check your integer sizes.
API Design: Timestamps vs ISO 8601
Two common conventions: Unix timestamp integers and ISO 8601 strings ("2026-02-24T08:00:00Z"). Both are valid. Timestamps are compact and easy to do math on. ISO 8601 is more readable in logs. If you use strings, always include a timezone suffix (Z for UTC) โ a bare datetime string in an API is ambiguous and will eventually cause a timezone bug.
Common Timestamp Pitfalls
The most frequent timestamp bugs stem from a handful of recurring mistakes. Mixing seconds and milliseconds is the classic: a server sends seconds, JavaScript interprets them as milliseconds, and the UI displays a date in January 1970. Always check the digit count before passing a timestamp to a date constructor.
Floating-point timestamps are another trap. Languages like Python's time.time() return a float, and rounding errors can shift your date by a full second at the boundary. Cast to an integer early if you only need second precision, or use Decimal if sub-second accuracy matters.
Negative timestamps are valid โ they represent dates before 1 January 1970. However, some libraries and databases reject them. Windows systems in particular can behave unpredictably with negative timestamps. If your application needs to handle historical dates before 1970, test the full stack from storage through display.
Testing and Debugging Tips
When a timestamp looks wrong, check three things: the unit (seconds vs milliseconds vs microseconds), the timezone assumption (UTC or local), and whether the value was truncated or rounded during serialisation. Logging the raw integer alongside the formatted date string is a cheap debugging habit that saves hours later.
For automated tests, avoid Date.now() or time.time() directly โ inject a clock dependency so you can freeze time. JavaScript testing frameworks like Jest provide jest.useFakeTimers(), and Python's freezegun library patches the standard library. Deterministic timestamps make tests reproducible and eliminate flaky time-window failures.
Sub-Second Precision: Milliseconds, Microseconds, Nanoseconds
Unix timestamps don't have to be whole seconds. Modern systems often need higher precision for logging, distributed systems, and financial applications.
| Unit | Digits | Example | Common Use |
|---|---|---|---|
| Seconds | 10 | 1740355200 | Date storage, session expiry, most APIs |
| Milliseconds | 13 | 1740355200000 | JavaScript Date.now(), browser APIs, most databases |
| Microseconds | 16 | 1740355200000000 | PostgreSQL timestamps, high-frequency logging |
| Nanoseconds | 19 | 1740355200000000000 | Linux kernel, Go time.UnixNano(), InfluxDB |
The digit count is the fastest way to identify what precision a timestamp uses. This is also why the detection trick works:
function detectTimestampUnit(ts) {
const s = String(Math.abs(ts));
if (s.length <= 10) return 'seconds';
if (s.length <= 13) return 'milliseconds';
if (s.length <= 16) return 'microseconds';
return 'nanoseconds';
}
// JavaScript always uses milliseconds
Date.now() // โ 1740355200000 (13 digits)
new Date().getTime() // โ 1740355200000
// Converting between units
const ms = seconds * 1000;
const seconds_from_ms = Math.floor(ms / 1000);
const seconds_from_ns = Math.floor(nanoseconds / 1_000_000_000);
PostgreSQL stores timestamps with microsecond precision. When inserting a JavaScript millisecond timestamp into PostgreSQL, divide by 1000 first, or use the to_timestamp() function which accepts fractional seconds: to_timestamp(1740355200.123).
