UtilitiesDeveloperTime

What Is a Unix Timestamp and How Do You Read One?

The authoritative reference for understanding Unix epoch time — what each one looks like, how to use it, and how timestamps interact in real systems.

6 min read

TL;DR — Key Points

Unix TimestampA single integer counting seconds elapsed since January 1, 1970, 00:00:00 UTC. Also called epoch time or POSIX time.
Seconds format10-digit number. Used by Linux, Python, PostgreSQL, REST APIs, and JWT tokens. Example: 1716278400 = May 21, 2026.
Milliseconds format13-digit number. Used by JavaScript Date.now(), Java, and frontend analytics. Same moment × 1000.
Negative timestampsRepresent dates before January 1, 1970. Example: -86400 = December 31, 1969. Valid in most modern systems.
Y2K38 Problem32-bit signed integers overflow at 2,147,483,647 (January 19, 2038). 64-bit systems are safe until year 292 billion.
UTCCoordinated Universal Time — the baseline with no timezone offset. All Unix timestamps are UTC, timezone-independent.

What Is a Unix Timestamp?

A Unix timestamp is a single integer that represents a specific moment in time by counting the number of seconds that have elapsed since January 1, 1970, 00:00:00 UTC — a reference point known as the Unix Epoch. It is the universal language of time in software, appearing in server logs, database records, API responses, authentication tokens, cache headers, analytics events, and virtually every system where two machines need to agree on when something happened.

When you see a number like 1716278400 in a JSON response or a database column, that is a Unix timestamp. It means exactly Wednesday, May 21, 2026, 00:00:00 UTC — not approximately, not in a particular timezone, but precisely that moment in time anywhere on Earth. The beauty of Unix timestamps is their simplicity: one number, no ambiguity, no timezone conversions required at storage time.

Unix timestamps are used by Linux and Unix operating systems natively, by Python's time.time() function, by PostgreSQL's EXTRACT(EPOCH) function, by most REST APIs including GitHub, Stripe, Twilio, and AWS, and by JWT tokens for expiry (exp) and issued-at (iat) claims. If you work with software in any capacity, you will encounter Unix timestamps regularly.

The key mental model is this: a Unix timestamp is just arithmetic. To find what timestamp corresponds to a moment, count the seconds from January 1, 1970. To find what moment a timestamp represents, add that many seconds to January 1, 1970. Everything else — timezones, daylight saving, leap years — is handled by conversion functions, not by the timestamp itself. The timestamp is always UTC, always an integer, always counting from the Epoch.

Seconds vs Milliseconds vs Microseconds

The single most common source of timestamp bugs in software is the confusion between seconds, milliseconds, and occasionally microseconds. The formats look similar but differ by factors of 1,000. Passing a milliseconds timestamp where seconds are expected — or the reverse — produces dates that are off by 1,000 years or are stuck in January 1970.

The fastest way to identify the format is by digit count. For any date between 2001 and 2286, a seconds timestamp is exactly 10 digits long, a milliseconds timestamp is exactly 13 digits, and a microseconds timestamp is 16 digits. If you see 1716278400 (10 digits) it is seconds. If you see 1716278400000 (13 digits) it is milliseconds. This rule works reliably for all modern dates.

JavaScript is the primary source of millisecond timestamps because Date.now() and new Date().getTime() both return milliseconds. This is why passing a seconds timestamp directly to new Date(1716278400) gives you January 20, 1970 — JavaScript interprets the 10-digit number as only about 19 days of milliseconds. The fix is always: new Date(1716278400 * 1000).

On the server side, the convention is almost universally seconds. Python's time.time(), PostgreSQL's EXTRACT(EPOCH), most REST APIs, and JWT tokens all use seconds. When you receive a 10-digit timestamp from a backend API, treat it as seconds. When you generate one in JavaScript frontend code and it is 13 digits, it is milliseconds.

Format Comparison Table

Use this reference when you encounter an unknown timestamp and need to identify its format and convert it correctly.

FeatureSecondsMillisecondsMicroseconds
Digit count10 digits13 digits16 digits
Example value171627840017162784000001716278400000000
Precision1 second1 millisecond1 microsecond
Common inLinux, Python, APIs, JWT, PostgreSQLJavaScript, Java, analyticsC/C++, high-frequency trading
Convert to date (JS)new Date(ts * 1000)new Date(ts)new Date(ts / 1000)
Convert to date (Py)datetime.utcfromtimestamp(ts)datetime.utcfromtimestamp(ts/1000)datetime.utcfromtimestamp(ts/1e6)

Negative Timestamps — Dates Before 1970

Unix timestamps are not constrained to positive numbers. A negative timestamp represents a moment that occurred before January 1, 1970, 00:00:00 UTC — before the Epoch. The further back in history, the more negative the value. This is mathematically consistent and is supported by most modern programming languages and databases.

-1 is one second before the Epoch: December 31, 1969, 23:59:59 UTC. -86400 is one full day before the Epoch (86,400 = 60 × 60 × 24): December 31, 1969, 00:00:00 UTC. -2208988800 corresponds to January 1, 1900, 00:00:00 UTC — the start of the 20th century.

JavaScript's new Date(-86400 * 1000) correctly returns December 31, 1969. Python's datetime.utcfromtimestamp(-86400) works correctly on most platforms. PostgreSQL handles negative epoch values in TO_TIMESTAMP() without issues. Problems arise in older C libraries, 32-bit unsigned integer storage, and certain embedded database drivers.

Negative timestamps have practical uses in archival systems, financial history databases, and genealogy software. If your application handles historical dates, test your full stack — language runtime, ORM, and database — with the value -86400 before committing to negative timestamp storage in production.

Famous Unix Timestamp Milestones

Several specific Unix timestamps have become culturally or technically significant in the developer community.

Timestamp 0 — The Unix Epoch (January 1, 1970, 00:00:00 UTC). The starting point of all Unix time. This is a valid timestamp — not a null or sentinel value. Any system that treats timestamp 0 as "missing" has a logic error that will eventually cause incidents when records are created at that exact UTC time in certain timezones.

946684800 — Y2K (January 1, 2000, 00:00:00 UTC). The famous Y2K concern was about two-digit year storage in COBOL and legacy systems, not Unix timestamps. Unix time handled Y2K perfectly: 946684800 is simply the next integer after 946684799. This is one of the strengths of Unix timestamps — no representation gap at round year boundaries.

1234567890 — The Sequential Milestone (February 13, 2009, 23:31:30 UTC). This timestamp became culturally significant because of its recognizable sequential pattern. Developers around the world organised gatherings and countdown timers to watch their system clocks tick past this number.

2147483647 — The Y2K38 Problem (January 19, 2038, 03:14:07 UTC). This is the most serious milestone. 2,147,483,647 is the maximum value of a 32-bit signed integer (2³¹ − 1). Systems storing Unix timestamps in signed 32-bit integers will overflow at this moment, wrapping to -2,147,483,648 and jumping to December 13, 1901. Affected systems include legacy embedded firmware, older Linux kernels, some IoT devices, and legacy database schemas using INT columns. Modern 64-bit systems are safe — they can hold timestamps until the year 292,277,026,596.

Converting Timestamps in Code

The conversion functions you need depend on your language and the direction of conversion. Here are the canonical patterns for the most common environments.

JavaScript (browser and Node.js): Current timestamp in milliseconds: Date.now(). In seconds: Math.floor(Date.now() / 1000). Seconds timestamp to Date: new Date(ts * 1000). To ISO 8601: new Date(ts * 1000).toISOString(). To IST locale string: new Date(ts * 1000).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" } ).

Python: Current timestamp in seconds: import time; int(time.time()). Timestamp to datetime: from datetime import datetime, timezone; datetime.fromtimestamp(ts, tz=timezone.utc). Always use timezone.utc — naive datetimes from utcfromtimestamp() are deprecated in Python 3.12+.

PostgreSQL: Current epoch: SELECT EXTRACT(EPOCH FROM NOW())::INTEGER. Integer to timestamp: SELECT TO_TIMESTAMP(1716278400). TIMESTAMPTZ to epoch: SELECT EXTRACT(EPOCH FROM created_at)::INTEGER FROM orders.

Bash / Linux: Current timestamp: date +%s. Convert to date (GNU/Linux): date -d @1716278400. Convert (macOS): date -r 1716278400.

Unix Timestamps in JWT Tokens

JSON Web Tokens use Unix timestamps in seconds for three standard claims: iat (issued at), exp (expiration), and nbf (not before), as specified in RFC 7519. These are always seconds, never milliseconds.

A typical JWT payload: { "sub": "user_abc123", "iat": 1716192000, "exp": 1716278400 }. To check expiry in JavaScript: const isExpired = exp < Math.floor(Date.now() / 1000). The division by 1000 is critical — Date.now() returns milliseconds, exp is seconds. This off-by-1000 error is one of the most common JWT bugs.

A common security practice is adding a clock skew buffer: exp + 300 < now (5 minutes). This prevents rejecting valid tokens due to minor clock drift between distributed servers — especially important in microservice architectures.

JWT headers and payloads are base64url-encoded, not encrypted — you can decode and read the exp timestamp without the secret key. This allows the frontend to proactively refresh tokens before they expire, rather than waiting for a 401 response from the API.

How to Decide Which Format You Need

Start with your use case, not the technology. Apply the following logic:

1

You see a 10-digit number in an API response or database column

Treat as seconds. Multiply by 1000 before passing to JavaScript Date().

2

You see a 13-digit number from Date.now() or a JS library

Treat as milliseconds. Pass directly to new Date() in JS. Divide by 1000 for Python.

3

You need to store a timestamp in a database for a modern application

Use a TIMESTAMP or TIMESTAMPTZ column, not an integer. Let the database handle it.

4

You need to check whether a JWT token has expired

Read the exp claim. Compare to Math.floor(Date.now() / 1000) in JS or time.time() in Python.

5

Your application handles dates before 1970 (historical data)

Use negative timestamps. Verify your language runtime and database support them before relying on them.

When Should You Use Each?

Consider these scenarios with the correct approach for each:

ScenarioCorrect Approach
Storing a user registration date in PostgreSQLTIMESTAMPTZ column (let the database handle it)
Parsing a GitHub API response with created_atISO 8601 string — use new Date(isoString) directly
Decoding a JWT token exp fieldUnix seconds — compare to Math.floor(Date.now() / 1000)
Reading a server log timestampUnix seconds (10 digits) — multiply by 1000 for JS
Measuring front-end performance with Date.now()Unix milliseconds (13 digits) — use directly in JS
Representing a historical date (pre-1970)Negative Unix timestamp or ISO 8601 string

Frequently Asked Questions

What is the difference between Unix time and UTC?

Unix time is a counting system — it counts seconds from the Epoch. UTC is a time standard — it defines what time it is at offset 0. Unix timestamps are always expressed in UTC, meaning the same number means the same moment everywhere regardless of local timezone. They are complementary: UTC tells you what time zone, Unix tells you the exact count.

Why does Unix time start in 1970?

January 1, 1970 was chosen when Unix was being developed at Bell Labs in the late 1960s and early 1970s. It was a convenient round date close to the time of development that fit within the 32-bit integer storage available. There is no deeper significance — it became the global standard purely by adoption of Unix across the industry.

How do I convert a Unix timestamp to a date in JavaScript?

If your timestamp is in seconds (10 digits): new Date(timestamp * 1000). If it is in milliseconds (13 digits): new Date(timestamp). To get ISO format: new Date(timestamp * 1000).toISOString(). The most common mistake is forgetting to multiply by 1000, which gives a date in January 1970.

How do I convert a Unix timestamp in Python?

From seconds: from datetime import datetime, timezone; datetime.fromtimestamp(ts, tz=timezone.utc). From milliseconds: datetime.fromtimestamp(ts / 1000, tz=timezone.utc). Always use timezone.utc to get a timezone-aware datetime rather than a naive one.

What is the Y2K38 problem?

Systems that store Unix timestamps as 32-bit signed integers will overflow at 2,147,483,647, which corresponds to January 19, 2038, 03:14:07 UTC. One second later, the counter rolls to -2,147,483,648, representing December 13, 1901. Modern 64-bit systems are not affected. The problem remains real for embedded systems, legacy firmware, and IoT devices.

Can Unix timestamps be negative?

Yes. A negative timestamp means the moment is before January 1, 1970. -1 is December 31, 1969, 23:59:59 UTC. -86400 is December 31, 1969, 00:00:00 UTC. Most modern languages and databases handle negative timestamps correctly.

Is a Unix timestamp always in seconds?

No — this is the source of constant confusion. The original Unix standard uses seconds, and most server-side systems follow this. However, JavaScript's Date API uses milliseconds, and many frontend libraries also use milliseconds. Always check the digit count: 10 digits = seconds, 13 digits = milliseconds.

What is the maximum Unix timestamp supported by modern systems?

For 32-bit signed integers: 2,147,483,647 (January 19, 2038). For 64-bit signed integers used by modern systems: 9,223,372,036,854,775,807 seconds — corresponding to the year 292,277,026,596. JavaScript's Date object supports timestamps up to approximately ±275,760 years from 1970.

Related Concepts

Related Tools

Timestamp to Date Converter

Convert any Unix timestamp to a human-readable date instantly. Auto-detects seconds vs milliseconds.

Open Tool →

Date to Timestamp Converter

Convert any date and time back to a Unix epoch timestamp in seconds or milliseconds.

Open Tool →

JWT Decoder

Decode any JWT token and inspect the exp, iat, and all other claims.

Open Tool →