Unix Timestamps and Date/Time Tools: The Complete Guide
· Cosyslabs
A Unix timestamp is the number of seconds elapsed since the Unix epoch: January 1, 1970, 00:00:00 UTC. Timestamps are timezone-independent — the same integer represents the same moment everywhere on Earth. They are the standard way to store and compare datetimes in databases, APIs, and logs.
The Unix Epoch
Unix epoch: 1970-01-01T00:00:00.000Z
Current time (2026-06-15T12:00:00Z): 1,750,000,000 (approximately)
Range of 32-bit Unix timestamps:
Min: -2,147,483,648 → 1901-12-13 20:45:52 UTC
Max: 2,147,483,647 → 2038-01-19 03:14:07 UTC (Year 2038 Problem)
Getting the Current Timestamp
// JavaScript — seconds
Math.floor(Date.now() / 1000)
// JavaScript — milliseconds (more common in JS ecosystem)
Date.now()
// JavaScript — nanoseconds (Node.js)
process.hrtime.bigint()
import time
from datetime import datetime, timezone
# Seconds (float)
time.time()
# Integer seconds
int(time.time())
# Milliseconds
int(time.time() * 1000)
# Using datetime
datetime.now(timezone.utc).timestamp()
# Linux/macOS shell
date +%s # seconds
date +%s%3N # milliseconds
-- PostgreSQL
SELECT EXTRACT(EPOCH FROM NOW())::BIGINT;
SELECT (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT;
-- MySQL
SELECT UNIX_TIMESTAMP();
SELECT UNIX_TIMESTAMP() * 1000;
-- SQLite
SELECT strftime('%s', 'now');
Converting Timestamps to Dates
// Seconds to Date
new Date(1750000000 * 1000)
// Mon Jun 15 2026 ...
// Milliseconds to Date
new Date(1750000000000)
// Format options
const date = new Date(timestamp * 1000);
date.toISOString(); // "2026-06-15T12:00:00.000Z"
date.toLocaleDateString(); // locale-specific
date.toUTCString(); // "Mon, 15 Jun 2026 12:00:00 GMT"
// Intl.DateTimeFormat — full control
const formatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "long",
timeZone: "America/New_York",
});
formatter.format(date); // "Monday, June 15, 2026 at 8:00:00 AM EDT"
from datetime import datetime, timezone
# Seconds to datetime (always UTC-aware)
dt = datetime.fromtimestamp(1750000000, tz=timezone.utc)
# datetime(2026, 6, 15, 12, 0, tzinfo=timezone.utc)
dt.isoformat() # "2026-06-15T12:00:00+00:00"
# Convert to specific timezone
from zoneinfo import ZoneInfo
eastern = dt.astimezone(ZoneInfo("America/New_York"))
# datetime(2026, 6, 15, 8, 0, tzinfo=ZoneInfo("America/New_York"))
import (
"fmt"
"time"
)
ts := int64(1750000000)
t := time.Unix(ts, 0).UTC()
fmt.Println(t.Format(time.RFC3339)) // "2026-06-15T12:00:00Z"
// Convert timezone
loc, _ := time.LoadLocation("America/New_York")
eastern := t.In(loc)
ISO 8601 Format
ISO 8601 is the international standard for date and time representation:
2026-06-15 date only
2026-06-15T12:00:00 local time (no timezone info — avoid in APIs)
2026-06-15T12:00:00Z UTC (Z = Zulu = UTC)
2026-06-15T08:00:00-04:00 With UTC offset
2026-06-15T12:00:00.000Z With milliseconds
Always include timezone information in API responses. A datetime string without timezone is ambiguous and causes bugs when clients are in different timezones.
// Good: always UTC in APIs
new Date().toISOString(); // "2026-06-15T12:00:00.000Z"
// Better: let the client convert to local time for display
// Store and transmit in UTC; display in user's local timezone
Timezone Handling
Timezones are the primary source of datetime bugs. Key rules:
- Store in UTC — always store datetimes as UTC in databases
- Display in local time — convert to user's timezone only for display
- Never store local time —
2026-06-15 08:00:00without timezone is ambiguous - Be aware of DST — offsets change twice a year in most regions
// WRONG — stores ambiguous local time
const appointment = new Date(2026, 2, 8, 2, 30); // 2:30 AM might not exist (DST)
// RIGHT — always use UTC
const appointment = new Date(Date.UTC(2026, 2, 8, 7, 30)); // 7:30 AM UTC = 2:30 AM EST
// Display in user's timezone
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
dateStyle: "medium",
timeStyle: "short",
});
formatter.format(appointment);
IANA Timezone Database
JavaScript's Intl API uses IANA timezone identifiers:
America/New_York America/Chicago America/Denver America/Los_Angeles
Europe/London Europe/Paris Europe/Berlin Europe/Moscow
Asia/Tokyo Asia/Shanghai Asia/Kolkata Asia/Singapore
Australia/Sydney Pacific/Auckland America/Sao_Paulo Africa/Cairo
Avoid abbreviations like EST, PST — they are ambiguous (EST is used by multiple timezones) and not valid IANA identifiers.
Date Arithmetic
// Add days
const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000);
// Add months (careful — months have different lengths)
const nextMonth = new Date(date);
nextMonth.setMonth(nextMonth.getMonth() + 1);
// Difference in days
const diffMs = date2.getTime() - date1.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
// Start of day in UTC
const startOfDay = new Date(date);
startOfDay.setUTCHours(0, 0, 0, 0);
// Start of week (Monday)
const dayOfWeek = date.getUTCDay(); // 0=Sun, 1=Mon, ...
const startOfWeek = new Date(date);
startOfWeek.setUTCDate(date.getUTCDate() - ((dayOfWeek + 6) % 7));
from datetime import datetime, timedelta, timezone
from dateutil.relativedelta import relativedelta # pip install python-dateutil
now = datetime.now(timezone.utc)
# Add days
tomorrow = now + timedelta(days=1)
# Add months (handles month-length differences correctly)
next_month = now + relativedelta(months=1)
# Difference
diff = date2 - date1 # timedelta
diff.days
diff.total_seconds()
# Start of day
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
The Year 2038 Problem
32-bit signed Unix timestamps overflow on January 19, 2038 at 03:14:07 UTC when the value reaches 2,147,483,647.
// 32-bit C code — will overflow in 2038
time_t t = time(NULL); // time_t is often int32 on 32-bit systems
Affected systems:
- 32-bit Linux systems using 32-bit
time_t - Older embedded systems (industrial equipment, IoT devices)
- Some database columns defined as
INTstoring Unix timestamps - Legacy C/C++ code on 32-bit platforms
Solutions:
- Use 64-bit integers for timestamps (covers 292 billion years)
- In SQL: use
BIGINTinstead ofINTfor timestamp columns - In PostgreSQL:
TIMESTAMP WITH TIME ZONEhandles this automatically - In MySQL:
DATETIMEcolumn type is safe (stores as 8 bytes internally)
-- Bad (will overflow in 2038 on 32-bit systems)
created_at INT DEFAULT UNIX_TIMESTAMP()
-- Good
created_at BIGINT DEFAULT (UNIX_TIMESTAMP() * 1000)
-- or better
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
Common Date Formats
// ISO 8601 (recommended for APIs)
"2026-06-15T12:00:00.000Z"
// HTTP Date (RFC 7231 — used in headers)
"Mon, 15 Jun 2026 12:00:00 GMT"
// RFC 2822 (email)
"Mon, 15 Jun 2026 08:00:00 -0400"
// US format
"06/15/2026"
// European format (ambiguous with US!)
"15/06/2026"
// SQL date
"2026-06-15 12:00:00"
// Relative time
"2 hours ago" // Do not store this — compute from timestamp
Database DateTime Best Practices
-- PostgreSQL: always use TIMESTAMPTZ (stores in UTC, displays in session timezone)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
-- MySQL: use DATETIME or TIMESTAMP (TIMESTAMP auto-converts to UTC)
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
-- NOT: created_at INT -- avoid storing raw Unix timestamps in SQL
-- Index for time-range queries
CREATE INDEX idx_created_at ON events(created_at);
-- Query last 7 days
SELECT * FROM events
WHERE created_at > NOW() - INTERVAL '7 days';
Tools
- Unix Timestamp Converter — convert between timestamps and human-readable dates
- Timezone Converter — convert times between any two timezones
- Date Difference Calculator — calculate days, hours, minutes between two dates
- Cron Parser — see when your cron job will next run