Introduction
A Unix timestamp is one of the most fundamental concepts in computing. It represents a single moment in time as a plain integer — the number of seconds elapsed since the Unix epoch (January 1, 1970, 00:00:00 UTC). This simple, universal format underpins everything from database records and API responses to log files and scheduled tasks.
Unlike human-readable date strings such as "April 9, 2024", a Unix timestamp has no timezone, no locale, and no ambiguity. The number 1712620800 means exactly one moment in time, everywhere on Earth. That property makes it the lingua franca of date and time in software engineering.
A Brief History of the Unix Epoch
The Unix operating system was developed at Bell Labs in the late 1960s and early 1970s by Ken Thompson, Dennis Ritchie, and colleagues. Time tracking was built into the kernel from the beginning, but the exact choice of epoch date evolved during early development.
Some of the earliest Unix versions used January 1, 1971 as the epoch. Others experimented with January 1, 1969. Eventually, January 1, 1970, 00:00:00 UTC was standardized — a "round" date close enough to the system's creation that timestamps for real events would be small, positive integers.
The choice of 1970-01-01 is, in essence, arbitrary. What matters is not the date itself but the consistency of the convention. Because every system agrees on the same epoch, timestamps can be exchanged between programs, languages, and platforms without any conversion overhead.
This convention was later formalized in POSIX (the Portable Operating System Interface), which defines a Unix timestamp as the number of non-leap seconds since 1970-01-01T00:00:00Z.
Seconds vs. Milliseconds
Not all "timestamps" are equal. The most important distinction is the unit:
- Unix timestamp (POSIX): seconds since epoch — e.g.,
1712620800 - JavaScript
Date: milliseconds since epoch — e.g.,1712620800000 - Java
System.currentTimeMillis(): milliseconds since epoch - Go
time.Now().Unix(): seconds;time.Now().UnixNano()gives nanoseconds - Python
time.time(): float seconds since epoch
The factor-of-1000 difference between seconds and milliseconds is a common source of bugs. Feeding a millisecond timestamp into code that expects seconds produces a date far in the future (year ~56,000); the reverse produces a date in January 1970.
Converting between them is straightforward:
const unix_ms = unix_s * 1000;
const unix_s = Math.floor(unix_ms / 1000);
A practical rule of thumb: if your timestamp is a 10-digit number, it is probably in seconds. If it is 13 digits, it is probably in milliseconds.
ISO 8601 Format and Variants
ISO 8601 is the international standard for representing date and time. It defines a family of unambiguous string formats:
2024-04-09 # Date only
2024-04-09T12:00:00 # Local datetime (no timezone info)
2024-04-09T12:00:00Z # UTC (Z = Zulu time = UTC+0)
2024-04-09T20:00:00+08:00 # With UTC offset (e.g., Asia/Shanghai)
2024-04-09T12:00:00.123Z # With milliseconds
2024-04-09T12:00:00.123456789Z # With nanoseconds
The T separates the date and time portions. The Z suffix (from "Zulu", NATO phonetic for UTC) means the time is in UTC. An offset like +08:00 means the local time is 8 hours ahead of UTC.
ISO 8601 is the recommended format for REST APIs, log files, and any system that exchanges time data across boundaries. It is human-readable, sortable as a string, and unambiguous.
Time Zones and UTC
UTC (Coordinated Universal Time) is the primary time standard for the world. It is not a timezone per se, but the baseline from which all time zones are defined. UTC+0 is the same as Greenwich Mean Time (GMT) during winter.
Time zones are expressed as offsets from UTC: UTC+5:30 (India), UTC-8 (US Pacific Standard Time), UTC+9 (Japan). However, raw offsets are not enough to fully describe a timezone, because offsets change with Daylight Saving Time (DST).
The IANA Time Zone Database (also called the tz database or zoneinfo) is the authoritative list of all world time zones. It uses identifiers like America/New_York, Europe/Berlin, Asia/Tokyo, and Asia/Kolkata. These identifiers encapsulate not just the current UTC offset but the full historical record of DST rules and political changes (e.g., when a country changes its timezone).
All major programming languages and operating systems include the IANA database:
- JavaScript (Node.js):
Intl.DateTimeFormatwith IANA identifiers - Python:
zoneinfomodule (Python 3.9+) orpytz - Java:
java.time.ZoneId(e.g.,ZoneId.of("Asia/Tokyo")) - Go:
time.LoadLocation("America/New_York")
Never use raw UTC offsets like +05:30 to represent time zones in application logic. Use IANA identifiers instead, because offsets change seasonally.
Daylight Saving Time (DST) Complications
Daylight Saving Time is the practice of advancing clocks by one hour during summer months to extend evening daylight. It is observed in most of North America and Europe, and in parts of South America, the Middle East, and Oceania. Many countries, including Japan, China, and India, do not observe DST at all.
DST introduces two classic anomalies:
Spring forward: Clocks jump from 2:00 AM directly to 3:00 AM. The 60-minute window 2:00–3:00 AM never exists in local time. If you schedule a job at 2:30 AM, it either runs at 3:30 AM or is skipped entirely, depending on the scheduler.
Fall back: Clocks return from 2:00 AM to 1:00 AM. The 60-minute window 1:00–2:00 AM occurs twice. If you record a log entry at "1:45 AM local time", it is ambiguous — it could be from the first or second occurrence of that hour.
Unix timestamps are entirely immune to DST because they are always relative to UTC. The number 1712620800 always refers to the same instant, regardless of where you are or what season it is.
The golden rule: always store and transmit timestamps in UTC. Convert to local time only at the presentation layer, immediately before displaying to a human user.
The Year 2038 Problem
The Year 2038 Problem (also called Y2K38 or the Unix Millennium Bug) is a software vulnerability similar in nature to the Y2K bug of 2000.
The root cause: many legacy systems store Unix timestamps as a 32-bit signed integer. The maximum value of a 32-bit signed integer is 2,147,483,647. This corresponds to:
2038-01-19 03:14:07 UTC
One second after that moment, a 32-bit signed integer overflows and wraps around to the most negative value: -2,147,483,648, which corresponds to 1901-12-13 20:45:52 UTC. Systems that overflow will suddenly believe the date is in 1901.
Systems potentially affected include:
- Legacy embedded systems and IoT devices compiled for 32-bit architectures
- Old database schemas using MySQL's
TIMESTAMPtype (which used 32-bit storage in versions before 8.0) - 32-bit Linux kernels (resolved in the kernel for 64-bit platforms)
- Some older file systems that record modification times as 32-bit integers
The solution is straightforward: migrate all timestamp storage to 64-bit signed integers. A 64-bit timestamp can represent dates up to approximately year 292,277,026,596 — well beyond any practical concern. Most modern 64-bit systems already handle this correctly.
Working with Timestamps in Different Languages
JavaScript
// Current time
const now = Date.now(); // milliseconds since epoch
const unix = Math.floor(now / 1000); // convert to seconds
// Parse a Unix timestamp
const date = new Date(unix * 1000);
console.log(date.toISOString()); // "2024-04-09T12:00:00.000Z"
console.log(date.toLocaleString("en-US", { timeZone: "America/New_York" }));
Python
import time
import datetime
# Current Unix timestamp (seconds)
unix = int(time.time())
# Convert to UTC datetime
dt = datetime.datetime.fromtimestamp(unix, tz=datetime.timezone.utc)
print(dt.isoformat()) # "2024-04-09T12:00:00+00:00"
# Using zoneinfo for IANA timezone
from zoneinfo import ZoneInfo
dt_local = dt.astimezone(ZoneInfo("America/New_York"))
print(dt_local.isoformat())
Go
package main
import (
"fmt"
"time"
)
func main() {
unix := time.Now().Unix() // int64 seconds
t := time.Unix(unix, 0).UTC()
fmt.Println(t.Format(time.RFC3339)) // "2024-04-09T12:00:00Z"
loc, _ := time.LoadLocation("Asia/Tokyo")
fmt.Println(t.In(loc).Format(time.RFC3339)) // "2024-04-09T21:00:00+09:00"
}
Java
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
long unix = Instant.now().getEpochSecond(); // seconds
Instant instant = Instant.ofEpochSecond(unix);
System.out.println(instant.toString()); // "2024-04-09T12:00:00Z"
ZonedDateTime zdt = instant.atZone(ZoneId.of("Europe/Berlin"));
System.out.println(zdt.toString());
Rust
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let unix = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs(); // u64 seconds
println!("{}", unix); // e.g. 1712620800
}
Common Timestamp Formats
| Format | Example | Used In |
|---|---|---|
| Unix (seconds) | 1712620800 |
Unix/Linux, POSIX APIs |
| Unix (milliseconds) | 1712620800000 |
JavaScript, Java |
| Unix (microseconds) | 1712620800000000 |
PostgreSQL, some APIs |
| Unix (nanoseconds) | 1712620800000000000 |
Go, Rust |
| ISO 8601 UTC | 2024-04-09T12:00:00Z |
REST APIs, databases |
| ISO 8601 with offset | 2024-04-09T20:00:00+08:00 |
Calendar apps |
| RFC 2822 | Tue, 09 Apr 2024 12:00:00 +0000 |
Email headers |
| RFC 3339 | 2024-04-09T12:00:00Z |
Internet protocols |
| HTTP date | Tue, 09 Apr 2024 12:00:00 GMT |
HTTP headers |
Timestamp Arithmetic
Because a Unix timestamp is just a number, arithmetic on timestamps is trivial.
Calculate the duration between two timestamps:
start = 1712620800
end = 1712707200
duration_seconds = end - start # 86400 seconds = exactly 1 day
Find a future or past date:
const now = Math.floor(Date.now() / 1000);
const oneWeekLater = now + 7 * 24 * 60 * 60; // +604800 seconds
const thirtyDaysAgo = now - 30 * 24 * 60 * 60; // -2592000 seconds
Common durations in seconds:
| Duration | Seconds |
|---|---|
| 1 minute | 60 |
| 1 hour | 3,600 |
| 1 day | 86,400 |
| 1 week | 604,800 |
| 30 days | 2,592,000 |
| 1 year (365 days) | 31,536,000 |
Note: for calendar-aware arithmetic (e.g., "add 1 month"), use a date library rather than raw seconds, because months have different lengths and DST can make some days 23 or 25 hours long.
Use Cases
Application logging: Log entries with Unix timestamps can be sorted, filtered, and compared across distributed systems running in different time zones — all without ambiguity.
REST APIs: Returning timestamps as Unix integers avoids timezone interpretation on the server side. The client reads the integer and formats it in the user's local timezone.
Databases: Storing timestamps as integers (or as ISO 8601 strings) is more portable than platform-specific date types. PostgreSQL's TIMESTAMPTZ stores in UTC internally; MySQL's DATETIME (preferred over TIMESTAMP) avoids the Y2038 limit.
Scheduled tasks and cron jobs: Calculating "run at 3:00 AM every day" in UTC avoids DST surprises. Many scheduling frameworks (Kubernetes CronJobs, GitHub Actions schedules) use UTC by convention.
Cache expiration and TTL: HTTP Cache-Control: max-age=3600 and Expires headers use absolute Unix timestamps or relative seconds; CDNs and browsers rely on precise timestamp math to invalidate caches.
Event sourcing and audit trails: Immutable event logs require timestamps that unambiguously order events. Unix timestamps, especially at nanosecond resolution, provide this guarantee even for high-throughput systems.
Best Practices
Always store timestamps in UTC. Never store local time in a database. Convert to local time only at the presentation layer.
Use 64-bit integers. Avoid
int32for timestamps in any new code. Even if your system currently handles dates only in the near future, 64-bit is the safe default.Use ISO 8601 for human-readable serialization. When you need a string representation in logs or APIs, ISO 8601 is unambiguous and sorts lexicographically.
Use IANA timezone identifiers, not offsets.
"America/New_York"is correct;"-05:00"is fragile because the offset changes twice a year.Validate timestamp units. Before using an external timestamp, verify whether it is in seconds, milliseconds, or another unit. A quick check: a 10-digit number is likely seconds; 13 digits is likely milliseconds.
Never parse dates with regex in production code. Use your language's standard library or a well-tested third-party library (e.g.,
date-fns,Luxonfor JavaScript;dateutilfor Python).Be careful with "midnight". In timezones that observe DST, midnight may not exist on certain dates (spring-forward transitions). Use noon (12:00 UTC) as a safe "representative" time for date-only calculations.
Test around DST transitions. If your application involves scheduling or time calculations, write tests that specifically exercise the spring-forward and fall-back boundaries for relevant timezones.
FAQ
Q: What does the Unix timestamp 0 represent?
A: January 1, 1970, 00:00:00 UTC — the Unix epoch. Negative timestamps represent dates before 1970.
Q: Can I use timestamps for sorting?
A: Yes. Because a Unix timestamp is a monotonically increasing integer, sorting by timestamp is equivalent to sorting chronologically.
Q: Is Unix time affected by leap seconds?
A: POSIX defines Unix time as if every day has exactly 86,400 seconds, meaning leap seconds are not counted. A POSIX timestamp is technically "Unix time" or "POSIX time", not "true" International Atomic Time (TAI). In practice, this rarely matters for application code.
Q: What is the maximum date representable with a Unix timestamp?
A: With a 64-bit signed integer, the maximum is year 292,277,026,596. With a 32-bit signed integer, it is 2038-01-19 03:14:07 UTC.
Q: How do I get the current Unix timestamp in my browser?
A: Math.floor(Date.now() / 1000) in the browser console returns the current Unix timestamp in seconds.
Q: Why does my timestamp show 1970-01-01 when I convert it?
A: You are almost certainly passing milliseconds to a function that expects seconds (or vice versa). Divide by 1000 if you see a date far in the future; multiply by 1000 if you see January 1, 1970.
Key Features
- Bidirectional Conversion: Convert raw seconds or milliseconds to dates, and human-readable dates back to timestamps.
- Local & UTC Support: View the result in your current local time or in Coordinated Universal Time (UTC).
- ISO 8601 Formatting: Automatically generates standard date strings for use in APIs and databases.
- Current Time Ticker: Stay up to date with a real-time display of the current Unix timestamp.
How to Use
- To Date: Paste a Unix timestamp into the input field to see the human-readable date.
- To Timestamp: Use the date picker or type a date string to generate the corresponding Epoch value.
- Switch Units: Toggle between Seconds (standard Unix) and Milliseconds (JavaScript/Java style).
- Copy Result: Use the copy buttons to grab the formatted date or numeric timestamp.
Common Use Cases
- Backend Development: Debugging database records or API responses that return raw timestamps.
- System Administration: Analyzing server logs that use Epoch time for event logging.
- Frontend Development: Converting API data for display in user interfaces.
- Task Scheduling: Calculating future timestamps for cron jobs or scheduled events.
Technical Background
The tool utilizes the JavaScript Date object and high-precision math to perform conversions. Since Unix timestamps are defined in UTC, the tool handles the offset calculations required to display local time based on the user's system settings. It supports both 10-digit (seconds) and 13-digit (milliseconds) formats, which are common in different programming ecosystems.
Frequently Asked Questions
- What is Epoch time? It is the starting point of the Unix time measurement: Jan 1, 1970.
- Does it handle the Year 2038 problem? Yes, modern 64-bit browsers handle dates far beyond 2038.
- Can I convert to specific timezones? The tool currently shows Local and UTC; we plan to add more timezone options soon.
Limitations
- Leap Seconds: Unix time does not account for leap seconds; however, for almost all software applications, this is the standard behavior.
- Precision: While it supports milliseconds, higher precision (nanoseconds) used in some systems (like Go or Rust) might be truncated.