Unix Timestamps: A Complete Developer Reference

# webdev# javascript# programming# beginners
Unix Timestamps: A Complete Developer ReferenceSnappy Tools

Unix timestamps are everywhere in software — database records, API responses, JWT expiry, log files,...

Unix timestamps are everywhere in software — database records, API responses, JWT expiry, log files, file metadata. Here's a complete reference for working with them across languages and environments.

What is a Unix timestamp?

A Unix timestamp is the number of seconds elapsed since the Unix epoch: January 1, 1970, 00:00:00 UTC.

0          → 1970-01-01 00:00:00 UTC
1000000000 → 2001-09-09 01:46:40 UTC
1746144000 → 2025-05-02 00:00:00 UTC
Enter fullscreen mode Exit fullscreen mode

Timestamps store a single integer instead of separate year, month, day, hour, minute, second fields — simpler to store, sort, and compare.

To convert between Unix timestamps and human-readable dates, the Unix Timestamp Converter converts in both directions and handles millisecond timestamps too.

Seconds vs milliseconds

JavaScript (and many other environments) uses milliseconds since the epoch, not seconds:

Date.now()  // 1746144000000 (milliseconds)
Enter fullscreen mode Exit fullscreen mode

APIs vary — some return seconds, some milliseconds. A quick way to tell: timestamps in milliseconds are roughly 1000× larger than those in seconds. In 2025:

  • Seconds: ~1,746,000,000 (10 digits)
  • Milliseconds: ~1,746,000,000,000 (13 digits)

When in doubt, check the magnitude.

Getting the current timestamp

// JavaScript
Date.now()                     // milliseconds
Math.floor(Date.now() / 1000)  // seconds
Enter fullscreen mode Exit fullscreen mode
# Python
import time
int(time.time())          # seconds
int(time.time() * 1000)   # milliseconds
Enter fullscreen mode Exit fullscreen mode
// Go
import "time"
time.Now().Unix()          // seconds
time.Now().UnixMilli()     // milliseconds
Enter fullscreen mode Exit fullscreen mode
# Shell
date +%s          # seconds
date +%s%3N       # milliseconds
Enter fullscreen mode Exit fullscreen mode
-- PostgreSQL
EXTRACT(EPOCH FROM NOW())::int   -- seconds
Enter fullscreen mode Exit fullscreen mode

Converting timestamp to date

// JavaScript
const ts = 1746144000;           // seconds
const date = new Date(ts * 1000); // Date constructor takes milliseconds
date.toISOString()               // "2025-05-02T00:00:00.000Z"
date.toLocaleDateString()        // locale-specific format
Enter fullscreen mode Exit fullscreen mode
# Python
from datetime import datetime, timezone

ts = 1746144000
dt = datetime.fromtimestamp(ts, tz=timezone.utc)
dt.isoformat()  # '2025-05-02T00:00:00+00:00'
Enter fullscreen mode Exit fullscreen mode

Common gotchas

Forgetting timezone

Timestamps are always UTC. The same timestamp shows different times in different timezones — correct behavior, but easy to misread.

const ts = 1746144000;
const d = new Date(ts * 1000);

d.toISOString()        // "2025-05-02T00:00:00.000Z" — always UTC
d.toLocaleDateString() // "5/1/2025" if you're in US/Pacific (UTC-7) — 11pm the day before!
Enter fullscreen mode Exit fullscreen mode

Off-by-one in seconds/milliseconds

The most common timestamp bug: passing seconds where milliseconds are expected.

// Wrong: passing seconds to Date constructor
new Date(1746144000)        // 1970-01-21 — the timestamp is tiny in millisecond space

// Right:
new Date(1746144000 * 1000) // 2025-05-02
new Date(1746144000000)     // also 2025-05-02
Enter fullscreen mode Exit fullscreen mode

Integer overflow in 32-bit systems

Unix timestamps stored as 32-bit signed integers overflow on January 19, 2038 at 03:14:07 UTC — the Y2K38 problem. 2³¹ - 1 = 2,147,483,647 seconds.

Modern systems use 64-bit integers, which won't overflow for billions of years. But legacy 32-bit databases and embedded systems still need updating.

Timestamp arithmetic

// Add 7 days
const now = Math.floor(Date.now() / 1000);
const oneWeekLater = now + (7 * 24 * 60 * 60);

// How many days ago?
function daysAgo(ts) {
  return Math.floor((Math.floor(Date.now() / 1000) - ts) / 86400);
}

// Is this timestamp in the past?
function isExpired(ts) {
  return ts < Math.floor(Date.now() / 1000);
}

// JWT expiry check (exp field is Unix seconds)
function isJwtExpired(exp) {
  return exp < Math.floor(Date.now() / 1000);
}
Enter fullscreen mode Exit fullscreen mode

Formatting for display

Use Intl.DateTimeFormat for locale-aware formatting:

const ts = 1746144000;
const d = new Date(ts * 1000);

// Relative time ("3 days ago")
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const diffDays = Math.floor((Date.now() / 1000 - ts) / 86400);
rtf.format(-diffDays, 'day');  // "3 days ago"

// Absolute time with explicit timezone
new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  year: 'numeric', month: 'short', day: 'numeric',
  hour: '2-digit', minute: '2-digit'
}).format(d);  // "May 1, 2025 at 08:00 PM"
Enter fullscreen mode Exit fullscreen mode

Storing timestamps in databases

Use 64-bit integer columns for Unix timestamps:

  • PostgreSQL: BIGINT or native TIMESTAMPTZ (preferred — stores UTC, converts on read)
  • MySQL: BIGINT UNSIGNED or DATETIME(3) for millisecond precision
  • SQLite: INTEGER (64-bit internally)
  • MongoDB: Date type (stores as UTC milliseconds)

TIMESTAMPTZ in PostgreSQL is generally the right choice for application timestamps — it handles timezone conversion automatically. Use raw BIGINT timestamps only when interoperating with systems that exchange raw numbers.


Unix timestamps are simple in principle — one integer representing a moment in UTC — but the seconds/milliseconds distinction and timezone handling cause most of the practical bugs. Get those two right and timestamp work becomes straightforward.