DevDash

By the DevDash Team · Last updated July 2026

Unix Timestamps Explained: Epoch Time for Developers

Every time you call Date.now()in JavaScript, check a JWT expiry, or set a cache TTL, you're working with Unix timestamps. They are the universal language of time in computing — a single integer that means the same thing regardless of where your server sits or what timezone your user is in. This guide covers what Unix timestamps are, how to work with them in every major language, the pitfalls that trip up even experienced developers, and why they still matter after more than fifty years.

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. That specific moment is known as the Unix epoch. It was chosen as the reference point when Unix was being developed at Bell Labs in the early 1970s, and the convention stuck because it was already baked into the operating system's kernel.

The concept is simple: take any moment in time, count the seconds between it and the epoch, and you get a plain integer. Right now, the current Unix timestamp is somewhere around 1.7 billion. The timestamp 0 is midnight on January 1, 1970 UTC. The timestamp 86400is exactly one day later, because there are 86,400 seconds in a day (60 × 60 × 24).

This representation has no concept of timezones, daylight saving time, or calendar quirks. A Unix timestamp of 1700000000refers to the exact same instant whether you're reading it in Tokyo, London, or New York. That property alone is why timestamps became the standard for storing and transmitting time in software systems.

Why Unix Timestamps Exist

Dates are deceptively complex. Between timezones, leap years, daylight saving transitions, and varying calendar systems, representing a moment in time as a human-readable string creates endless room for ambiguity. Unix timestamps solve this by reducing time to a single number with four key properties:

Timezone independence. The timestamp is always UTC. There is no timezone offset embedded in the value, no DST flag, no locale. Two servers in different countries comparing timestamps are comparing the same thing.

Natural sortability. Because timestamps are integers, sorting events chronologically is just sorting numbers. No date parsing required.

Compact storage. A 10-digit integer takes 4 bytes as a 32-bit int or 8 bytes as a 64-bit int. An ISO 8601 string like 2024-01-15T09:30:00.000Z takes 24 bytes minimum.

Universal support. Every programming language, database, and operating system understands Unix timestamps. They are the lingua franca of time.

Seconds vs Milliseconds: The Most Common Timestamp Bug

The Unix timestamp convention uses seconds. Most backend languages, APIs, and databases follow this convention. A typical seconds-based timestamp is a 10-digit number like 1700000000.

JavaScript broke from this convention. Date.now() and new Date().getTime() both return millisecondssince the epoch — a 13-digit number like 1700000000000. Java's System.currentTimeMillis() does the same.

This mismatch causes constant bugs. If you pass a millisecond timestamp to an API that expects seconds, the server interprets it as a date tens of thousands of years in the future. If you pass seconds to JavaScript's new Date(), you get a date in January 1970 — just a few days after the epoch. When your dates look wildly wrong, check whether you're mixing seconds and milliseconds first. It is almost always the issue.

// JavaScript: milliseconds to seconds
const timestampSeconds = Math.floor(Date.now() / 1000);

// JavaScript: seconds to Date object
const date = new Date(timestampSeconds * 1000);

Getting the Current Unix Timestamp

Every language has a built-in way to get the current timestamp. Here's how to do it in the most common environments, all returning seconds since the epoch:

// JavaScript (seconds)
Math.floor(Date.now() / 1000)

# Python
import time
int(time.time())

// PHP
time()

# Bash
date +%s

-- PostgreSQL
SELECT EXTRACT(EPOCH FROM NOW())::INTEGER;

-- MySQL
SELECT UNIX_TIMESTAMP();

If you need a quick conversion without writing code, the DevDash Unix Timestamp Converter shows the current epoch time and lets you convert between timestamps and human-readable dates instantly in your browser.

Converting Between Timestamps and Dates

Converting a timestamp to a readable date and back is something you do constantly during debugging. Here are the patterns you'll reach for most often:

// JavaScript: timestamp to readable string
new Date(1700000000 * 1000).toISOString()
// "2023-11-14T22:13:20.000Z"

// JavaScript: date string to timestamp
Math.floor(new Date("2023-11-14T22:13:20Z").getTime() / 1000)
// 1700000000

# Python: timestamp to datetime
from datetime import datetime, timezone
datetime.fromtimestamp(1700000000, tz=timezone.utc)
# datetime(2023, 11, 14, 22, 13, 20, tzinfo=timezone.utc)

# Python: datetime to timestamp
int(datetime(2023, 11, 14, 22, 13, 20, tzinfo=timezone.utc).timestamp())
# 1700000000

Notice that every example explicitly uses UTC. This is deliberate. If you omit the timezone, the conversion uses your system's local time, which silently produces a different timestamp depending on where the code runs. Always be explicit about UTC when converting.

Timezone Handling: Why Timestamps Are Always UTC

This is the single most important thing to understand about timestamps: a Unix timestamp has no timezone. It is, by definition, the number of seconds since the epoch in UTC. There is no such thing as a "local" timestamp.

The timezone only enters the picture when you display a timestamp to a human. The timestamp 1700000000is always the same moment. But when you render it for a user, it shows as 10:13 PM on November 14 in London, 5:13 PM the same day in New York, and 7:13 AM on November 15 in Tokyo. The underlying value never changes — only the human-readable representation does.

Most date bugs come from confusing these two layers. A common mistake: parsing a date string without a timezone, which makes the runtime assume local time. Your code works perfectly on your laptop in one timezone, then breaks in production running on a server in UTC. The fix is straightforward: always store and transmit timestamps (or UTC strings), and only convert to local time at the display layer.

The Year 2038 Problem

Many older systems store Unix timestamps as 32-bit signed integers. A signed 32-bit integer has a maximum value of 2,147,483,647, which corresponds to January 19, 2038 at 03:14:07 UTC. One second later, the integer overflows. Instead of incrementing to 2,147,483,648, it wraps around to -2,147,483,648 — which the system interprets as December 13, 1901.

This is sometimes called the "Y2K38" problem or the "Epochalypse." While it sounds like a distant concern, it already affects any system that works with future dates. Certificate expiry dates, mortgage calculations, insurance policies, and long-lived subscriptions can already hit the 2038 boundary.

The fix is to use 64-bit integers, which can represent dates up to approximately 292 billion years from now. Most modern operating systems, databases, and languages have already made this transition. Linux moved to 64-bit timestamps, and JavaScript's Date object uses 64-bit floats internally. However, embedded systems, legacy databases, and older C code using time_t as a 32-bit int remain vulnerable. If you maintain any such systems, auditing your timestamp storage type is worth doing now rather than waiting for silent failures.

Timestamps in Databases

Databases offer multiple ways to store time, and choosing the wrong one creates subtle bugs that surface months later. Here are the main options:

TIMESTAMP (PostgreSQL/MySQL). Stored internally as a UTC value. PostgreSQL's TIMESTAMP WITH TIME ZONEconverts to UTC on insert and converts back to the session's timezone on read. MySQL's TIMESTAMP behaves similarly. This is usually what you want for event times.

DATETIME (MySQL) / TIMESTAMP WITHOUT TIME ZONE (PostgreSQL).Stores the literal date and time values you provide, with no timezone conversion. If you insert "2024-01-15 09:30:00", that's exactly what you get back, regardless of what timezone the server is in. Use this for dates that are inherently local, like "the user's birthday" or "store opening hours."

INTEGER column. Storing a raw Unix timestamp as an integer gives you maximum portability and zero ambiguity. There is no timezone conversion to worry about, no database-specific behavior to learn. The tradeoff is that your data is not human-readable in raw SQL queries. Many teams use this approach for operational fields like created_at and expires_at.

Timestamps in APIs and Logs: Unix vs ISO 8601

When designing an API or structured log format, you'll choose between Unix timestamps and ISO 8601 strings. Both are valid, and most real-world systems use a mix.

Unix timestampsare compact, unambiguous, and trivial to compare. They work well for operational fields: token expiry times, cache TTLs, rate limit reset windows, and internal event ordering. They also avoid parsing overhead — an integer comparison is faster than parsing two date strings and comparing them.

ISO 8601 strings (like 2024-01-15T09:30:00Z) are self-documenting. A developer reading a JSON response can immediately understand the date without opening a converter. They also carry precision information (date-only vs date-time) and are the standard in most public REST APIs.

A pragmatic approach: use ISO 8601 for fields that humans read (response bodies, user-facing data) and Unix timestamps for fields that machines consume (token claims, cache headers, log correlation IDs). Whatever you choose, document it clearly and be consistent within a single API.

Common Debugging Scenarios

Timestamps show up in nearly every debugging session. Here are the scenarios you'll encounter most often:

Expired JWTs. A JSON Web Token's exp claim is a Unix timestamp in seconds. If your auth system rejects a token that should be valid, decode it with a JWT decoder and compare the exp value against the current timestamp. Clock skew between services is a frequent culprit.

Cache TTLs.Cache expiry is typically set as "current timestamp plus N seconds." If your cache is expiring too quickly or not at all, check whether the TTL was calculated in seconds or milliseconds, and whether the system clock is accurate on the machine that set the cache entry.

Rate limiting windows. Rate limiters often use timestamps to define sliding windows. If a user reports being rate-limited despite low usage, check whether the window boundaries are being calculated correctly and whether the timestamp source is consistent across replicas.

Log correlation. When tracing a request across multiple services, timestamps let you reconstruct the order of events. If your logs use different timestamp formats or precisions across services, correlating them becomes painful. Standardize on UTC Unix timestamps with millisecond precision for structured logs.

For all of these, a quick way to sanity-check a timestamp is to paste it into the DevDash timestamp converter and see if the resulting date matches your expectation.

Negative Timestamps: Dates Before 1970

The Unix epoch is January 1, 1970, but that doesn't mean timestamps can't represent earlier dates. A negative timestamp simply counts seconds backward from the epoch. December 31, 1969 at 23:59:00 UTC is -60. January 1, 1900 is approximately -2208988800.

Most modern languages handle negative timestamps correctly. JavaScript's new Date(-60000)returns December 31, 1969. Python's datetime.fromtimestamp(-2208988800, tz=timezone.utc) returns January 1, 1900.

However, some databases and older systems reject or mishandle negative timestamps. MySQL's TIMESTAMP type only supports dates from 1970 to 2038. If you need to store historical dates before 1970, use a DATETIME column or a signed BIGINT instead.

Convert Unix Timestamps Instantly

The DevDash Unix Timestamp Converter shows you the current epoch time, converts between timestamps and human-readable dates, and handles both seconds and milliseconds. Everything runs in your browser — no data leaves your machine.

Open Timestamp Converter