← All articles · May 6, 2026 · fmt.hjlabs.in

Unix Timestamp Conversion: Every Format Explained

A Unix timestamp is the number of seconds since 1970-01-01 00:00:00 UTC. Sounds simple. In practice, it is the source of more bugs than any other date representation in software.

Seconds vs milliseconds

The original Unix timestamp counts seconds (10 digits today: 1745020800). JavaScript and many APIs use milliseconds (13 digits: 1745020800000). The "Date" library you're using will pick one — if you mix them, you'll see dates in 1970 (treating ms as seconds) or in the year 50000 (treating seconds as ms).

Quick check: 10 digits = seconds, 13 digits = milliseconds.

Timezones

Unix timestamps are always UTC. The "timezone" is applied when you format them for display. There is no such thing as a "PST timestamp" or "IST timestamp" — those are display formats, not storage formats.

Common bug: server in UTC, client in IST (UTC+5:30). Server stores 1745020800 (which is 2026-04-19 00:00:00 UTC). Client formats it as 2026-04-19 05:30:00 IST. Both are correct; they're the same instant.

ISO 8601: the human-readable cousin

ISO 8601 format: 2026-04-19T00:00:00Z (Z = Zulu = UTC). Equivalent to Unix timestamp 1745020800. Always include the timezone — Z for UTC or +05:30 for offset.

Why ISO 8601 in addition to Unix timestamps?

Conversion in different languages

// JavaScript
const ts = Math.floor(Date.now() / 1000);  // seconds
const tsMs = Date.now();                        // ms
const iso = new Date(ts * 1000).toISOString();

# Python
import time, datetime
ts = int(time.time())                          # seconds
iso = datetime.datetime.fromtimestamp(ts, datetime.timezone.utc).isoformat()

// Go
ts := time.Now().Unix()                       // seconds
tsMs := time.Now().UnixMilli()                // ms
iso := time.Now().UTC().Format(time.RFC3339)

# Bash
date +%s                                 # seconds
date +%s%3N                              # ms
date -u +"%Y-%m-%dT%H:%M:%SZ"             # ISO 8601

The Y2038 problem

32-bit signed integers overflow at 2038-01-19 03:14:07 UTC. After that, a 32-bit Unix timestamp wraps to negative numbers (which represent dates in 1901).

Status in 2026:

Action: audit your DB schemas. PostgreSQL timestamptz, MySQL BIGINT for storing as ms.

Microseconds and nanoseconds

Leap seconds

Earth's rotation is irregular, so UTC occasionally adds a leap second. The Unix timestamp standard does not account for leap seconds — the count of seconds in a day is always 86,400 even when reality has 86,401.

Implication: during a leap second, two real-world moments share the same Unix timestamp. For most applications this is invisible. For high-frequency trading, distributed systems, or scientific timing — use TAI (atomic time) or PTP.

Common bugs

  1. Mixing seconds and milliseconds. Detect: dates in 1970 or year 50000.
  2. Server in non-UTC timezone. Always run servers in UTC and convert at display time.
  3. SQL timestamp without timezone. Use timestamptz in Postgres, store as UTC always.
  4. Daylight saving transitions. Spring-forward day has 23 hours; fall-back has 25. Don't assume "1 day = 86400 seconds" in local time.
  5. Calling new Date('2026-01-15'). Without explicit timezone, JavaScript interprets as UTC. With time but no offset, it's local. Always include Z or offset.

Storing timestamps

Best practice in 2026:

Display formatting

Convert at the edges (just before display, just after parse). Internally, store and pass UTC.

// Always show user's local time
const date = new Date(timestampMs);
date.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });

Tools

Bottom line

Always store UTC. Always include explicit timezone in serialised dates. Be paranoid about seconds vs milliseconds. Audit your databases for 32-bit timestamps. Use ISO 8601 for human-facing APIs.

Try the free dev tools suite

15+ tools. 100% client-side. No signup. No tracking.

Browse all tools →

More from the blog