What is a Unix Timestamp?
A Unix timestamp is the number of seconds (or milliseconds) elapsed since the Unix epoch: January 1, 1970, 00:00:00 UTC. It is a single integer that uniquely identifies any moment in time, completely timezone-independent.
Epoch: 1970-01-01T00:00:00Z
1716000000 = 2024-05-18T08:00:00Z (approximately)
1716000000000 = 2024-05-18T08:00:00Z in milliseconds (JavaScript)Unix timestamps are used in:
- APIs — most REST APIs return createdAt and updatedAt as ISO strings or timestamps
- JWT tokens — iat (issued at), exp (expiry), nbf (not before) are Unix timestamps
- Log files — structured logs use timestamps for sorting and filtering
- Databases — PostgreSQL EXTRACT(EPOCH FROM ...), MySQL UNIX_TIMESTAMP()
- Rate limiting — retry-after headers and token bucket timestamps
Seconds vs Milliseconds — The Source of Most Bugs
| Format | Digits | Example | Used by |
|---|---|---|---|
| Seconds | 10 | 1716000000 | Most Unix tools, JWT, Go, Python |
| Milliseconds | 13 | 1716000000000 | JavaScript Date, Java, Android |
| Microseconds | 16 | 1716000000000000 | PostgreSQL precision, some C libs |
| Nanoseconds | 19 | 1716000000000000000 | Go time.UnixNano(), Linux kernel |
The most common bug: passing a second timestamp to JavaScript's new Date() — you get January 1970 instead of 2024. Always check the digit count first.
Go — Working with Timestamps
import (
"fmt"
"time"
)
// Get current timestamp
nowSeconds := time.Now().Unix() // int64 seconds since epoch
nowMillis := time.Now().UnixMilli() // int64 milliseconds (Go 1.17+)
nowMicros := time.Now().UnixMicro() // int64 microseconds (Go 1.17+)
nowNano := time.Now().UnixNano() // int64 nanoseconds
// Convert timestamp to time.Time
ts := int64(1716000000)
t := time.Unix(ts, 0).UTC() // seconds, no nanoseconds
fmt.Println(t.Format(time.RFC3339)) // => "2024-05-18T08:00:00Z"
// From milliseconds
tsMs := int64(1716000000000)
t2 := time.UnixMilli(tsMs).UTC()
// From nanoseconds
tsNs := int64(1716000000000000000)
t3 := time.Unix(0, tsNs).UTC()
// Formatting options
fmt.Println(t.Format("2006-01-02")) // => "2024-05-18"
fmt.Println(t.Format("2006-01-02 15:04:05")) // => "2024-05-18 08:00:00"
fmt.Println(t.Format(time.RFC3339)) // => "2024-05-18T08:00:00Z"
fmt.Println(t.Format(time.RFC1123)) // => "Sat, 18 May 2024 08:00:00 UTC"
// Convert to specific timezone
ist := time.FixedZone("IST", 5*60*60+30*60) // UTC+5:30
istTime := t.In(ist)
fmt.Println(istTime.Format("2006-01-02 15:04:05 MST")) // => "2024-05-18 13:30:00 IST"
// Compare timestamps
future := time.Now().Add(24 * time.Hour)
isExpired := time.Now().After(future) // falsePython — Working with Timestamps
import time
from datetime import datetime, timezone, timedelta
# Get current timestamp
now_s = int(time.time()) # seconds
now_ms = int(time.time() * 1000) # milliseconds
# Convert timestamp to datetime (always use tz=timezone.utc!)
ts = 1716000000
dt = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt.isoformat()) # => "2024-05-18T08:00:00+00:00"
print(dt.strftime("%Y-%m-%d")) # => "2024-05-18"
print(dt.strftime("%Y-%m-%d %H:%M:%S UTC")) # => "2024-05-18 08:00:00 UTC"
# From milliseconds
ts_ms = 1716000000000
dt2 = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc)
# Convert datetime to timestamp
dt3 = datetime(2024, 5, 18, 8, 0, 0, tzinfo=timezone.utc)
ts_back = int(dt3.timestamp()) # => 1716019200 (approximately)
# Convert to specific timezone
ist = timezone(timedelta(hours=5, minutes=30)) # UTC+5:30
dt_ist = dt.astimezone(ist)
print(dt_ist.strftime("%Y-%m-%d %H:%M:%S %Z")) # => "2024-05-18 13:30:00 UTC+05:30"
# WRONG — naive datetime (no timezone) is unreliable
dt_naive = datetime.fromtimestamp(ts) # uses local timezone — avoid in serversJavaScript — Working with Timestamps
// Get current timestamp
const nowMs = Date.now(); // milliseconds
const nowS = Math.floor(Date.now() / 1000); // seconds
// Parse millisecond timestamp
const ts = 1716000000000;
const d = new Date(ts);
console.log(d.toISOString()); // => "2024-05-18T08:00:00.000Z"
// Parse SECOND timestamp (the most common mistake!)
const tsS = 1716000000;
const d2 = new Date(tsS * 1000); // multiply by 1000 to get ms
console.log(d2.toISOString()); // => "2024-05-18T08:00:00.000Z"
// Check digit count before parsing
function parseTimestamp(ts) {
const n = Number(ts);
// 10 digits = seconds, 13 digits = milliseconds
return new Date(String(ts).length <= 10 ? n * 1000 : n);
}
// Format for display
d.toLocaleDateString("en-IN", { timeZone: "Asia/Kolkata" });
// => "18/5/2024"
d.toLocaleString("en-IN", {
timeZone: "Asia/Kolkata",
dateStyle: "medium",
timeStyle: "short",
});
// => "18 May 2024, 1:30 pm"
// Get the timestamp from an ISO string
const ts2 = new Date("2024-05-18T08:00:00Z").getTime(); // => 1716019200000 (ms)SQL Timestamp Operations
-- PostgreSQL — convert to epoch
SELECT EXTRACT(EPOCH FROM NOW())::BIGINT; -- => 1716000000 (seconds)
SELECT EXTRACT(EPOCH FROM NOW()) * 1000; -- milliseconds
-- PostgreSQL — convert epoch to timestamp
SELECT TO_TIMESTAMP(1716000000); -- => 2024-05-18 08:00:00+00
-- MySQL
SELECT UNIX_TIMESTAMP(); -- current timestamp (seconds)
SELECT FROM_UNIXTIME(1716000000); -- => 2024-05-18 08:00:00
-- Filter by time range
SELECT * FROM events
WHERE created_at BETWEEN
TO_TIMESTAMP(1716000000) AND TO_TIMESTAMP(1716086400);Storing Timestamps in APIs — ISO vs Epoch
| Format | Example | Pros | Cons |
|---|---|---|---|
| ISO 8601 string | "2024-05-18T08:00:00Z" | Human-readable, unambiguous timezone | Larger payload, must parse |
| Unix epoch (seconds) | 1716000000 | Compact, easy arithmetic | Not human-readable, ms vs s confusion |
| Unix epoch (ms) | 1716000000000 | JavaScript-native | Not human-readable |
Recommendation: Use ISO 8601 strings ("2024-05-18T08:00:00Z") in REST APIs for human-readable dates. Use Unix timestamps for performance-critical internal systems, JWT claims, and cache TTLs.
The Year 2038 Problem
32-bit signed integers can represent timestamps up to January 19, 2038, 03:14:07 UTC — after that they overflow to negative values. This affects:
- Old embedded systems using time_t (32-bit)
- Some legacy MySQL TIMESTAMP columns (internally 32-bit)
- Any system that casts a timestamp to a 32-bit int
Modern systems are safe: Go's int64 handles timestamps until year 292,277,026,596. JavaScript uses 64-bit floats (safe until year 275,760). PostgreSQL TIMESTAMPTZ is safe until year 294,276.
Check your legacy code for any int32 or uint32 timestamp arithmetic.
Use JSONKit's Unix Timestamp Converter to convert any timestamp to a human-readable date in any timezone — paste a number and get the ISO date, or enter a date and get the epoch value.