Unix timestamps are the foundation of time handling in computing. Simple yet powerful, understanding them is essential for any developer working with dates and times.
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, at 00:00:00 UTC—the Unix Epoch.
Unix Timestamp: 1705312800
Represents: January 15, 2024, at 10:00:00 UTC
That's it—just a number counting seconds. No timezones, no date formats, no ambiguity. Because the value is a single integer, it means the exact same instant on every machine on the planet; only the way you format it for a human changes from place to place.
The Unix Epoch
The Epoch is the reference point:
January 1, 1970, 00:00:00 UTC = 0
Why 1970? It was chosen when Unix was being developed in the early 1970s. A recent date meant smaller numbers, and it was already in the past (no negative values for current times). Timestamps before 1970 are simply negative numbers.
Some notable timestamps:
| Timestamp | Date | Event |
|---|---|---|
| 0 | Jan 1, 1970 | Unix Epoch |
| 1000000000 | Sep 9, 2001 | "Unix Billennium" |
| 1234567890 | Feb 13, 2009 | Memorable number |
| 1672531200 | Jan 1, 2023 | Start of 2023 (UTC) |
| 2000000000 | May 18, 2033 | Two billion |
| 2147483647 | Jan 19, 2038 | Y2K38 problem |
Curious about the numbers you keep seeing hard-coded in tests and configs? See our companion reference, Notable Unix Timestamps, for the story behind each one.
Seconds vs Milliseconds
This is the single most common source of "my date is in 1970" or "my date is in the year 55000" bugs. Unix time is officially measured in seconds since the epoch, but many environments—JavaScript most notably—work in milliseconds.
// Seconds (10 digits for current dates)
1705312800
// Milliseconds (13 digits for current dates)
1705312800000
How to tell them apart: count the digits. For any date in the current era (roughly 2001 through 2286):
| Digits | Unit | Example |
|---|---|---|
| 10 | Seconds | 1705312800 |
| 13 | Milliseconds | 1705312800000 |
| 16 | Microseconds | 1705312800000000 |
| 19 | Nanoseconds | 1705312800000000000 |
JavaScript's Date.now() and getTime() return milliseconds; most other languages and Unix system calls return seconds.
// JavaScript — milliseconds
Date.now() // 1705312800000
// Python — seconds (with a fractional part)
import time
time.time() // 1705312800.123
Converting between the two is just multiply or divide by 1000:
// Seconds -> milliseconds
const ms = seconds * 1000;
// Milliseconds -> seconds (round down to a whole second)
const seconds = Math.floor(ms / 1000);
A reliable rule of thumb: if you feed a value into a date library and land on January 1970, you passed seconds where milliseconds were expected (too small by 1000×). If you land thousands of years in the future, you passed milliseconds where seconds were expected.
What the Trailing Z Means
When you see an ISO 8601 timestamp like 2024-01-15T10:00:00Z, that trailing Z is not a typo or a placeholder—it stands for Zulu time, the military and aviation name for UTC (Coordinated Universal Time). It means "zero offset from UTC."
2024-01-15T10:00:00Z <- UTC, exactly
2024-01-15T10:00:00+00:00 <- identical meaning, written out
2024-01-15T11:00:00+01:00 <- same instant, expressed in a +1 zone
So Z is shorthand for +00:00. All three lines above can describe the same moment; the third just formats it for an observer one hour ahead of UTC.
A timestamp without a Z or an explicit offset (for example 2024-01-15T10:00:00) has no timezone information at all. It is ambiguous—different systems may assume UTC, may assume local time, or may reject it. Whenever you emit an ISO string for storage or an API, include the Z (or an explicit offset) so the value is unambiguous.
Unix Time vs ISO 8601
Both represent the same underlying instant; they trade off machine-friendliness against human-readability.
| Unix timestamp | ISO 8601 | |
|---|---|---|
| Example | 1705312800 |
2024-01-15T10:00:00Z |
| Human-readable | No | Yes |
| Timezone shown | Always UTC (implicit) | Explicit (Z or offset) |
| Best for | Storage, math, comparisons, APIs | Logs, display, config, debugging |
| Sortable as text | Yes (fixed width) | Yes (if same format) |
| Size | ~4-8 bytes as an integer | 20+ bytes as a string |
The two are trivially interchangeable:
// Unix seconds -> ISO 8601
new Date(1705312800 * 1000).toISOString();
// "2024-01-15T10:00:00.000Z"
// ISO 8601 -> Unix seconds
Math.floor(new Date('2024-01-15T10:00:00Z').getTime() / 1000);
// 1705312800
Many APIs happily provide both so neither the machine nor the human has to do the conversion:
{
"created_at": 1705312800,
"created_at_iso": "2024-01-15T10:00:00Z"
}
Timezones and UTC
A Unix timestamp is always UTC. There is no such thing as a "New York timestamp"—1705312800 is the same instant whether you're in Tokyo, London, or Los Angeles. The timezone only enters the picture when you format the number for a human.
const timestamp = 1705312800;
const date = new Date(timestamp * 1000);
// UTC
date.toUTCString(); // "Mon, 15 Jan 2024 10:00:00 GMT"
// Local time (depends on the viewer's timezone)
date.toString(); // "Mon Jan 15 2024 05:00:00 GMT-0500 (EST)"
Both lines describe the same moment; only the presentation differs. This is why timestamps are so convenient for storage—you never have to record a timezone alongside them.
Best Practices
- Store in UTC: timestamps are inherently UTC—store the raw number.
- Convert on display: render times in the viewer's local timezone at the last possible moment.
- Accept with a timezone: when receiving times, require an explicit offset (or a trailing
Z), or clearly document that bare values are treated as UTC.
import time
from datetime import datetime
# Storing
created_at = int(time.time()) # UTC timestamp
# Displaying
local_time = datetime.fromtimestamp(created_at, tz=user_timezone)
Why Timestamps Are Useful
1. Language/System Agnostic
Every language can work with integers:
// JavaScript
new Date(1705312800 * 1000)
// Python
datetime.fromtimestamp(1705312800)
// SQL
FROM_UNIXTIME(1705312800)
2. Easy Date Math
Adding and subtracting time is just arithmetic:
const now = 1705312800;
const oneHourLater = now + 3600; // Add 3600 seconds
const oneDayAgo = now - 86400; // Subtract 86400 seconds
const oneWeekLater = now + 604800; // 7 * 24 * 60 * 60
3. Easy Comparison and Sorting
if (timestamp1 < timestamp2) {
// timestamp1 is earlier
}
Because they're plain integers, timestamps sort chronologically automatically—no locale-aware string parsing required.
4. Storage Efficient
An integer takes less space than a date string:
- Timestamp: 4 bytes (32-bit) or 8 bytes (64-bit)
- ISO 8601 string: 20+ bytes
Converting Timestamps to Dates
JavaScript
// Timestamp to Date
const date = new Date(1705312800 * 1000); // Note: multiply by 1000
console.log(date.toISOString()); // "2024-01-15T10:00:00.000Z"
// Date to timestamp
const timestamp = Math.floor(Date.now() / 1000); // Divide by 1000 for seconds
Python
from datetime import datetime, timezone
# Timestamp to Date
date = datetime.fromtimestamp(1705312800, tz=timezone.utc) # UTC
# Date to timestamp
timestamp = int(datetime.now(tz=timezone.utc).timestamp())
Unix/Linux Command Line
# Current timestamp
date +%s
# Timestamp to date
date -d @1705312800
# Date to timestamp
date -d "2024-01-15 10:00:00" +%s
The Year 2038 Problem (32-bit vs 64-bit)
For decades, many systems stored Unix time in a signed 32-bit integer. A signed 32-bit value can count up to 2,147,483,647—and that ceiling arrives sooner than you'd think.
Maximum 32-bit timestamp: 2147483647
Represents: January 19, 2038, at 03:14:07 UTC
One second later, the counter overflows and wraps around to the most negative value, which a 32-bit system interprets as December 13, 1901. This is the Year 2038 problem (sometimes "Y2K38")—the same class of bug as Y2K, but rooted in integer size rather than two-digit years.
Who's Affected?
- Embedded systems and IoT devices with long service lives
- Legacy 32-bit software and firmware
- Databases with 32-bit timestamp columns
- Old programming-language runtimes
The Fix: 64-bit
Modern systems store time in a signed 64-bit integer, which pushes the overflow roughly 292 billion years into the future—longer than the age of the universe.
// Old (32-bit problem)
time_t timestamp; // May be 32-bit on legacy platforms
// New (64-bit safe)
int64_t timestamp; // Definitely 64-bit
Most current operating systems, languages, and databases already use 64-bit time. The remaining risk lives in old hardware, on-disk formats, and network protocols that pinned the field at 32 bits—so audit long-lived systems well before 2038.
Timestamps in Different Contexts
JavaScript
Date.now() // 1705312800000 (milliseconds)
Math.floor(Date.now() / 1000) // 1705312800 (seconds)
new Date('2024-01-15T10:00:00Z').getTime() / 1000 // parse ISO -> seconds
new Date(timestamp * 1000) // seconds -> Date
Python
import time
from datetime import datetime, timezone
time.time() # 1705312800.123456 (seconds, float)
int(time.time()) # 1705312800 (whole seconds)
datetime(2024, 1, 15, 10, tzinfo=timezone.utc).timestamp() # date -> ts
datetime.fromtimestamp(1705312800, tz=timezone.utc) # ts -> date
SQL
-- MySQL
SELECT UNIX_TIMESTAMP(); -- Current
SELECT UNIX_TIMESTAMP('2024-01-15 10:00:00'); -- Specific date
SELECT FROM_UNIXTIME(1705312800); -- To datetime
-- PostgreSQL
SELECT EXTRACT(EPOCH FROM NOW()); -- Current
SELECT TO_TIMESTAMP(1705312800); -- To timestamptz
Frequently Asked Questions
What is a Unix timestamp?
A Unix timestamp is the number of seconds that have elapsed since the Unix Epoch—January 1, 1970, at 00:00:00 UTC. It's a single integer that identifies an exact instant in time, the same on every computer, with no timezone or formatting attached.
Is a Unix timestamp in seconds or milliseconds?
By definition, Unix time is measured in seconds. However, JavaScript (Date.now()) and many web APIs use milliseconds. Count the digits to tell them apart: a current-era value with 10 digits is seconds, 13 digits is milliseconds. Divide milliseconds by 1000 to get seconds, or multiply seconds by 1000 to get milliseconds.
What does the Z at the end of a timestamp mean?
In an ISO 8601 string like 2024-01-15T10:00:00Z, the Z means "Zulu time," which is UTC / zero offset (+00:00). It tells you the time is expressed in UTC with no timezone adjustment. A timestamp with no Z and no offset is ambiguous and should be avoided.
What is the difference between Unix time and ISO 8601?
Both describe the same instant. Unix time is a bare integer count of seconds—compact, easy to do math with, and ideal for storage and APIs. ISO 8601 (like 2024-01-15T10:00:00Z) is a human-readable string that shows the date, time, and timezone explicitly—ideal for logs, display, and debugging. They convert to each other trivially.
What is the year 2038 problem?
The Year 2038 problem (Y2K38) is an overflow bug in systems that store Unix time as a signed 32-bit integer. The maximum such value, 2147483647, is reached at 03:14:07 UTC on January 19, 2038. One second later the counter wraps to a negative number and is misread as 1901. The fix is to store time as a 64-bit integer, which is safe for hundreds of billions of years.
Summary
Unix timestamps are simple yet powerful:
- What: seconds since January 1, 1970 UTC
- Format: a plain integer—10 digits for seconds, 13 for milliseconds
- The Z: means Zulu/UTC, a zero offset in ISO 8601
- Why: language-agnostic, easy math, sortable, compact
- Watch out for: seconds vs milliseconds, and the 32-bit Y2K38 overflow
- Best practice: store as UTC timestamps, format to local time on display
They're the universal language of time in computing—learn them once, use them everywhere.
Need to convert timestamps? Try our Unix Timestamp Converter!