Unix Timestamps Explained

What they are, how to convert them, and common pitfalls

ConvertersFebruary 28, 20266 min readBy Keyur Patel

Numbers like 1714521600 look meaningless until you realize they represent every moment in computing history since 1970. I debugged a timezone bug at 3 AM once because someone confused seconds with milliseconds — trust me, you want to understand this properly.

What Is a Unix Timestamp?

A Unix timestamp (also called Unix time, POSIX time, or epoch time) is the number of seconds that have elapsed since January 1, 1970, 00:00:00 UTC — known as the Unix epoch. It is a single integer that represents any point in time, regardless of timezone.

Unix Epoch

0

Jan 1, 1970 00:00:00 UTC

Example

1714521600

May 1, 2024 00:00:00 UTC

Why Use Unix Timestamps?

  • Timezone-independent: A timestamp represents the same moment in time everywhere. No timezone conversion needed for storage.
  • Easy arithmetic: Calculating the difference between two times is just subtraction. Adding 86400 seconds adds exactly one day.
  • Universal: Every programming language, database, and operating system understands Unix timestamps.
  • Compact: A single integer is more efficient to store and transmit than a formatted date string.
  • Sortable: Timestamps sort chronologically as integers — no date parsing needed.

Converting Timestamps in Code

JavaScript

Get current timestamp:

Math.floor(Date.now() / 1000)  // seconds
Date.now()  // milliseconds

Convert to date:

new Date(1714521600 * 1000).toISOString()
// → '2024-05-01T00:00:00.000Z'

Python

Get current timestamp:

import time
int(time.time())  # seconds since epoch

Convert to date:

from datetime import datetime
datetime.utcfromtimestamp(1714521600)
# → datetime(2024, 5, 1, 0, 0)

SQL (MySQL)

Get current timestamp:

UNIX_TIMESTAMP()  -- current timestamp
UNIX_TIMESTAMP('2024-05-01')  -- specific date

Convert to date:

FROM_UNIXTIME(1714521600)
-- → '2024-05-01 00:00:00'

Seconds vs Milliseconds — A Common Bug

Different systems use different timestamp resolutions. This is a frequent source of bugs:

Seconds (10 digits)

1714521600

Used by: Unix/Linux, Python, PHP, most APIs

Milliseconds (13 digits)

1714521600000

Used by: JavaScript Date.now(), Java, some databases

⚠️ If a date shows as year 1970 or year 56000, you likely mixed up seconds and milliseconds. Divide by 1000 or multiply by 1000 to fix it.

The Year 2038 Problem

Systems that store Unix timestamps as a 32-bit signed integer can only represent dates up to January 19, 2038, 03:14:07 UTC (timestamp 2,147,483,647). After this point, the value overflows to a negative number, causing dates to wrap back to 1901.

Modern systems use 64-bit integers, which can represent dates billions of years into the future. If you are working with legacy systems or embedded devices, verify they use 64-bit timestamps.

Working with Dates and Times in Software

Date and time handling is notoriously difficult in software. Timezones, daylight saving time, leap years, and inconsistent formats create a minefield of subtle bugs. This section covers the most important concepts and tools for handling dates correctly.

UTC vs Local Time: The Core Pitfall

The most fundamental rule of date handling: always store and transmit dates in UTC, and only convert to local time for display. Mixing UTC and local time in your data layer is the root cause of most timezone bugs.

// ❌ Wrong — stores local time, breaks across timezones
const now = new Date().toLocaleString()

// ✅ Correct — store as UTC ISO string or Unix timestamp
const now = new Date().toISOString()  // "2024-05-01T12:00:00.000Z"
const timestamp = Math.floor(Date.now() / 1000)  // Unix timestamp

// ✅ Convert to local time only for display
const localTime = new Date(timestamp * 1000).toLocaleString("en-US", {
  timeZone: "America/New_York"
})

ISO 8601 Format

ISO 8601 is the international standard for date and time representation. It is unambiguous, sortable as a string, and universally understood by programming languages and databases. Always use ISO 8601 when exchanging dates between systems:

FormatExampleNotes
Date only2024-05-01YYYY-MM-DD
Date and time (UTC)2024-05-01T12:00:00ZZ suffix means UTC
Date and time with offset2024-05-01T14:00:00+02:00Explicit timezone offset
Date and time (milliseconds)2024-05-01T12:00:00.000ZIncludes milliseconds

Database Timestamp Types

PostgreSQL offers two timestamp types with an important difference:

  • TIMESTAMP (without time zone): Stores the date and time as-is, with no timezone information. The value is interpreted as local time, which can cause confusion when your server's timezone changes.
  • TIMESTAMPTZ (with time zone): Stores the timestamp in UTC internally and converts to the session timezone on retrieval. This is almost always the correct choice for application data.

Always use TIMESTAMPTZ in PostgreSQL. In MySQL, use DATETIME with explicit UTC storage, or TIMESTAMPwhich stores in UTC but has a range limited to 2038.

Daylight Saving Time Bugs

Daylight saving time (DST) transitions cause some of the most insidious date bugs. When clocks spring forward, one hour is skipped — times like 2:30 AM do not exist. When clocks fall back, one hour is repeated — 1:30 AM occurs twice. Code that assumes 24 hours in a day or 60 minutes in an hour will fail on DST transition days.

The safest approach: always work in UTC internally and only apply timezone conversions at the display layer. Never add 86400 seconds to a local time to get "tomorrow" — use a date library that understands DST transitions instead.

JavaScript Date Libraries

The built-in JavaScript Date object is notoriously difficult to work with. These libraries provide a much better developer experience:

date-fns~13KB (tree-shakeable)

Functional, immutable date utilities. Each function is a separate import, making it highly tree-shakeable. Best choice for most projects.

Luxon~23KB

Built by the Moment.js team as its modern successor. Excellent timezone support via the Intl API. Immutable and chainable.

Day.js~2KB

Tiny Moment.js-compatible API. Great for projects where bundle size is critical. Plugin system for timezone support.

Temporal (upcoming)Native

The TC39 Temporal proposal will add a comprehensive date/time API to JavaScript natively. Available as a polyfill today.

Convert Timestamps Online

⏱️ Timestamp Converter

Convert Unix timestamps to human-readable dates and back — instantly

Unix Timestamps in Different Programming Languages

Every major programming language provides built-in ways to work with Unix timestamps. Below is a comprehensive reference showing how to get the current timestamp and convert timestamps back to human-readable dates in the most popular languages and environments. Bookmark this section as a quick reference when switching between languages.

JavaScript

JavaScript's Date object works in milliseconds, so you need to divide by 1000 to get seconds. The Date.now() method returns milliseconds since epoch, while constructing a Date from a Unix timestamp requires multiplying by 1000.

// Get current Unix timestamp (seconds)
const timestamp = Math.floor(Date.now() / 1000)
// → 1714521600

// Get current timestamp (milliseconds)
const ms = Date.now()
// → 1714521600000

// Convert timestamp to Date object
const date = new Date(1714521600 * 1000)
// → Wed May 01 2024 00:00:00 GMT+0000

// Format as ISO string
date.toISOString()
// → "2024-05-01T00:00:00.000Z"

// Format as locale string
date.toLocaleString("en-US", { timeZone: "America/New_York" })
// → "4/30/2024, 8:00:00 PM"

Python

Python provides the time module for simple timestamp operations and the datetime module for full date manipulation. The time.time() function returns the current Unix timestamp as a float with microsecond precision.

import time
from datetime import datetime, timezone

# Get current Unix timestamp
timestamp = int(time.time())
# → 1714521600

# Convert timestamp to datetime (UTC)
dt = datetime.fromtimestamp(1714521600, tz=timezone.utc)
# → datetime(2024, 5, 1, 0, 0, tzinfo=timezone.utc)

# Format as string
dt.strftime("%Y-%m-%d %H:%M:%S %Z")
# → "2024-05-01 00:00:00 UTC"

# Convert datetime back to timestamp
int(dt.timestamp())
# → 1714521600

PHP

PHP's time() function returns the current Unix timestamp directly in seconds. The date() function formats a timestamp into a human-readable string using format characters similar to C's strftime.

<?php
// Get current Unix timestamp
$timestamp = time();
// → 1714521600

// Convert timestamp to formatted date
echo date("Y-m-d H:i:s", 1714521600);
// → "2024-05-01 00:00:00"

// Convert date string to timestamp
$ts = strtotime("2024-05-01 00:00:00");
// → 1714521600

// Using DateTime class
$dt = new DateTime("@1714521600");
$dt->setTimezone(new DateTimeZone("UTC"));
echo $dt->format("Y-m-d H:i:s T");
// → "2024-05-01 00:00:00 UTC"
?>

Java

Java 8+ provides the java.time package with Instant for working with timestamps. The Instant.now() method returns the current moment in time with nanosecond precision, and getEpochSecond() extracts the Unix timestamp.

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

// Get current Unix timestamp (seconds)
long timestamp = Instant.now().getEpochSecond();
// → 1714521600

// Convert timestamp to Instant
Instant instant = Instant.ofEpochSecond(1714521600L);

// Convert to a specific timezone
ZonedDateTime zdt = instant.atZone(ZoneId.of("America/New_York"));
// → 2024-04-30T20:00-04:00[America/New_York]

// Format as string
String formatted = zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
// → "2024-04-30T20:00:00-04:00"

SQL (MySQL and PostgreSQL)

SQL databases provide built-in functions for converting between timestamps and dates. MySQL uses UNIX_TIMESTAMP() and FROM_UNIXTIME(), while PostgreSQL uses EXTRACT(EPOCH FROM ...) and to_timestamp().

-- MySQL: Get current timestamp
SELECT UNIX_TIMESTAMP();
-- → 1714521600

-- MySQL: Convert timestamp to date
SELECT FROM_UNIXTIME(1714521600);
-- → '2024-05-01 00:00:00'

-- MySQL: Convert date to timestamp
SELECT UNIX_TIMESTAMP('2024-05-01 00:00:00');
-- → 1714521600

-- PostgreSQL: Get current timestamp
SELECT EXTRACT(EPOCH FROM NOW())::INTEGER;
-- → 1714521600

-- PostgreSQL: Convert timestamp to date
SELECT to_timestamp(1714521600);
-- → 2024-05-01 00:00:00+00

-- PostgreSQL: Convert date to timestamp
SELECT EXTRACT(EPOCH FROM TIMESTAMP '2024-05-01 00:00:00');
-- → 1714521600

Command Line (Bash)

The date command on Linux and macOS provides quick timestamp operations directly in the terminal. This is useful for scripting, debugging, and quick conversions without opening a code editor.

# Get current Unix timestamp
date +%s
# → 1714521600

# Convert timestamp to human-readable date (Linux)
date -d @1714521600
# → Wed May  1 00:00:00 UTC 2024

# Convert timestamp to human-readable date (macOS)
date -r 1714521600
# → Wed May  1 00:00:00 UTC 2024

# Get timestamp for a specific date (Linux)
date -d "2024-05-01" +%s
# → 1714521600

# PowerShell (Windows)
[DateTimeOffset]::Now.ToUnixTimeSeconds()
# → 1714521600

# PowerShell: Convert timestamp to date
[DateTimeOffset]::FromUnixTimeSeconds(1714521600).DateTime
# → Wednesday, May 1, 2024 12:00:00 AM

Timestamp Pitfalls That Cause Production Bugs

I've debugged hundreds of time-related bugs in production. These are the ones that keep coming back. Learn from them so you don't have to debug them at 3 AM:

⚠️ Seconds vs milliseconds mismatch

JavaScript's Date.now() returns milliseconds (13 digits), but most backend systems expect seconds (10 digits). Passing milliseconds where seconds are expected creates dates in the year 55,000+.

Fix: Always be explicit: Math.floor(Date.now() / 1000) for seconds. Name your variables unixSeconds or unixMs to make intent clear.

⚠️ Timezone assumption in date-only comparisons

Comparing '2024-05-01' in UTC vs local time can shift by a day. A user in UTC-8 creates something at 11 PM local time → that's the next day in UTC.

Fix: Store timestamps, not dates. When you need date-only comparison, convert to the user's timezone first, then extract the date component.

⚠️ Daylight Saving Time (DST) transitions

At DST boundaries, clocks jump forward or back. 'Add 24 hours' doesn't always equal 'next day' — it could skip a day or land on the same day.

Fix: Use timezone-aware date libraries (date-fns-tz, Luxon, dayjs with tz plugin). Never manually add 86400 seconds for 'next day' — use date arithmetic that accounts for DST.

⚠️ Year 2038 problem (32-bit overflow)

32-bit signed integers overflow on January 19, 2038 at 03:14:07 UTC. Legacy systems using 32-bit timestamps will roll over to negative numbers (December 1901).

Fix: Use 64-bit timestamps. In databases, use BIGINT for Unix timestamps. Most modern languages use 64-bit by default, but verify embedded systems and legacy code.

⚠️ Floating-point precision in timestamps

JavaScript Numbers can't precisely represent all integer timestamps beyond 2^53. Millisecond timestamps approaching this limit lose precision.

Fix: Use BigInt for nanosecond timestamps. For standard second/millisecond timestamps, JavaScript Numbers are fine (2^53 in seconds is year 285 million).

⚠️ Server clock drift in distributed systems

Different servers have slightly different clocks. Comparing timestamps from two servers can show events in wrong order.

Fix: Use NTP for clock sync. For ordering guarantees, use logical clocks (vector clocks, Lamport timestamps) or centralized timestamp services.

Timestamps in API Design: Best Practices

How you handle timestamps in your API affects every consumer. Get it wrong and you'll be fielding timezone-related bug reports forever. Here's what works:

Always return timestamps in UTC — let the client convert to local time for display
Use ISO 8601 format (2024-05-01T00:00:00Z) for human-readable fields, Unix timestamps for machine-to-machine
Include timezone offset in ISO strings: 2024-05-01T00:00:00+00:00 or 2024-05-01T00:00:00Z
Document whether your API uses seconds or milliseconds — it's never obvious from the value alone
Store created_at, updated_at, and deleted_at as UTC timestamps in every database table
For scheduling (appointments, reminders), store both the UTC timestamp AND the user's timezone — DST rules change
Use monotonic clocks for measuring durations (process.hrtime() in Node.js) — wall clocks can jump backward
For audit logs and legal compliance, store both the UTC timestamp and the user's local time at that moment

API Response Example — Consistent Timestamp Format

{
  "id": "order_abc123",
  "status": "shipped",
  "created_at": "2024-05-01T14:30:00Z",      // ISO 8601 UTC
  "created_at_unix": 1714574200,               // Unix seconds (for machine processing)
  "shipped_at": "2024-05-02T09:15:00Z",
  "estimated_delivery": "2024-05-05",          // Date-only (no time component)
  "timezone": "America/New_York"               // User's timezone for display
}

// Client-side: Convert UTC to local time for display
const local = new Date("2024-05-01T14:30:00Z").toLocaleString("en-US", {
  timeZone: "America/New_York"
})
// → "5/1/2024, 10:30:00 AM"

Summary

Here's the golden rule: store everything in UTC, display in local time, and never assume a 10-digit number and a 13-digit number are the same thing. The seconds vs milliseconds trap has caused more production incidents than I care to admit. Get those two rules right and timestamps stop being scary.