All articles
Engineering

The Developer’s Cheat Sheet: Unix Timestamps in JS, Python, Go, PHP, and SQL

Stop fighting timezones. Here is the ultimate cheat sheet for getting, converting, and formatting Unix timestamps across JavaScript, Python, Go, PHP, and SQL databases.

Share:

Dates and times become fragile the moment they move between systems. A backend stores a creation timestamp in UTC seconds. An API serialises it as a JSON integer. A frontend receives the value and calls new Date()— which expects milliseconds. The result is a date somewhere in the year 55000, a confused user, and a developer reaching for a throwaway conversion script.

Timezone drift, string formatting mismatches, and seconds-vs-milliseconds errors are among the most common causes of subtle production bugs. They rarely crash anything outright. Instead, they cause cache invalidation failures, incorrect JWT expiry checks, misleading log sequences, and scheduling jobs that fire at the wrong hour.

This article is a practical, cross-language cheat sheet. It covers how to get, convert, and format Unix timestamps in JavaScript, Python, Go, PHP, PostgreSQL, and MySQL — with the gotchas that actually trip engineers up in production.

TL;DR

  • A Unix timestamp is the number of seconds since January 1, 1970 UTC (the Unix epoch). It represents a single, unambiguous moment.
  • It is useful for machine-readable time transfer across services, APIs, logs, tokens, and caches.
  • The biggest recurring bug: confusing seconds and milliseconds. JavaScript uses milliseconds by default; most other systems use seconds.
  • Format for humans at the presentation layer, not at the storage or transport layer.

Need to debug a timestamp right now? Use the CodeAva Unix Timestamp Converter to decode or encode epochs instantly — with auto-detection for seconds vs milliseconds and JWT time claim inspection.

Why developers use Unix timestamps

A Unix timestamp gives you a single integer that refers to an exact moment in time, regardless of timezone, locale, or formatting convention. That makes it ideal for situations where ambiguity is expensive:

  • Logs and event streams— ordering events across distributed services with a sortable numeric value.
  • JWT claimsiat, exp, and nbf are all Unix seconds.
  • Cache TTLs and expiry— comparing a stored timestamp against the current time with a simple numeric check.
  • Message queues and scheduling— cron triggers, delayed jobs, and retry logic all benefit from unambiguous integer time.
  • JSON APIs— integers are smaller and faster to parse than formatted date strings.

Unix time is not always the best storage format

For relational database queries, date arithmetic, indexing, and reporting, native timestamp types (like PostgreSQL's timestamptz or MySQL's DATETIME) are usually more practical. Unix timestamps are especially useful at system boundaries, transport layers, and in debugging workflows— not necessarily as the canonical storage format for every column.

The cheat sheet table

Bookmark this. Every row is copy-pasteable.

Language / SystemCurrent Unix secondsMilliseconds variantConvert back to readable date
JavaScript / Node.jsMath.floor(Date.now() / 1000)Date.now()new Date(ts * 1000).toISOString()
Pythonint(time.time())int(time.time() * 1000)datetime.fromtimestamp(ts, tz=timezone.utc)
Gotime.Now().Unix()time.Now().UnixMilli()time.Unix(ts, 0).UTC()
PHPtime()(int)(microtime(true) * 1000)(new DateTimeImmutable())->setTimestamp($ts)
PostgreSQLEXTRACT(EPOCH FROM NOW())EXTRACT(EPOCH FROM NOW()) * 1000to_timestamp(ts)
MySQLUNIX_TIMESTAMP()UNIX_TIMESTAMP() * 1000FROM_UNIXTIME(ts)

Seconds vs milliseconds: the most common timestamp bug

This single distinction causes more debugging time than almost any other date-related issue. The problem is simple: JavaScript's Date.now() returns milliseconds. Most backend languages, JWT claims, and database functions return seconds.

A 10-digit number like 1700000000is seconds — November 14, 2023. A 13-digit number like 1700000000000is milliseconds — the same moment. If you accidentally treat the 13-digit value as seconds:

// Wrong: treating milliseconds as seconds
new Date(1700000000000 * 1000)
// → year 55873

// Correct: milliseconds go directly into Date()
new Date(1700000000000)
// → 2023-11-14T22:13:20.000Z

The same mistake works in reverse. If a backend receives a JavaScript millisecond value and stores it in a seconds-only column, you lose three digits of precision and the timestamp refers to a moment thousands of years in the future.

Rule of thumb: always be explicit about the unit at system boundaries. Document whether your API fields use seconds or milliseconds. When in doubt, count the digits.

The CodeAva Unix Timestamp Converter auto-detects whether a pasted value is seconds or milliseconds and shows both interpretations side by side.

Implementation by language

JavaScript / Node.js

JavaScript's Date object operates in milliseconds everywhere. Date.now() returns the current millisecond timestamp. The Date constructor expects milliseconds. If you need Unix seconds for an API or JWT, divide and floor.

// Current Unix timestamp in seconds
const unixSeconds = Math.floor(Date.now() / 1000);

// Current Unix timestamp in milliseconds
const unixMs = Date.now();

// Convert Unix seconds back to a Date
const date = new Date(unixSeconds * 1000);

// Format in UTC
console.log(date.toISOString());
// → "2026-03-28T20:00:00.000Z"

// Format in local time
console.log(date.toLocaleString());
// → "3/28/2026, 10:00:00 PM" (varies by locale)

// Format with explicit timezone using Intl
const formatter = new Intl.DateTimeFormat("en-GB", {
  timeZone: "Europe/London",
  dateStyle: "long",
  timeStyle: "short",
});
console.log(formatter.format(date));

Main gotcha: forgetting to multiply by 1000 when passing Unix seconds to new Date(), or forgetting to divide by 1000 when sending Date.now() to a seconds-based API.

Python

Python's time.time() returns a float in seconds. For integer seconds, wrap in int(). The datetime module handles conversion back to readable dates. Always use timezone-aware objects to avoid silent local-time assumptions.

import time
from datetime import datetime, timezone

# Current Unix timestamp in seconds
unix_seconds = int(time.time())

# Current Unix timestamp in milliseconds
unix_ms = int(time.time() * 1000)

# Convert Unix seconds back to a timezone-aware datetime
dt = datetime.fromtimestamp(unix_seconds, tz=timezone.utc)
print(dt.isoformat())
# → "2026-03-28T20:00:00+00:00"

# Format in a specific timezone (Python 3.9+ with zoneinfo)
from zoneinfo import ZoneInfo

dt_local = dt.astimezone(ZoneInfo("America/New_York"))
print(dt_local.strftime("%Y-%m-%d %H:%M:%S %Z"))
# → "2026-03-28 16:00:00 EDT"

Main gotcha: datetime.utcfromtimestamp() returns a naive datetime with no timezone info, which makes it easy to mix up with local time downstream. Prefer datetime.fromtimestamp(ts, tz=timezone.utc) for explicit UTC awareness.

Go

Go's time package is explicitly typed. You get seconds, milliseconds, or nanoseconds through distinct methods. The type system prevents most accidental unit mismatches.

package main

import (
	"fmt"
	"time"
)

func main() {
	// Current Unix timestamp in seconds
	unixSec := time.Now().Unix()

	// Current Unix timestamp in milliseconds
	unixMs := time.Now().UnixMilli()

	// Convert Unix seconds back to a time.Time
	t := time.Unix(unixSec, 0).UTC()
	fmt.Println(t.Format(time.RFC3339))
	// → "2026-03-28T20:00:00Z"

	// Format with a custom layout
	fmt.Println(t.Format("2006-01-02 15:04:05 MST"))
	// → "2026-03-28 20:00:00 UTC"

	fmt.Println(unixSec, unixMs)
}

Main gotcha:Go's reference time layout uses the literal date Mon Jan 2 15:04:05 MST 2006. New Go developers often try YYYY-MM-DD patterns from other languages, which silently produce wrong output.

PHP

PHP's time() returns the current Unix timestamp in seconds. Modern PHP codebases should use DateTimeImmutable for conversion and formatting rather than legacy date()/strftime() patterns.

<?php

// Current Unix timestamp in seconds
$unixSeconds = time();

// Current Unix timestamp in milliseconds
$unixMs = (int)(microtime(true) * 1000);

// Convert Unix seconds back to a DateTimeImmutable
$dt = (new DateTimeImmutable())
    ->setTimestamp($unixSeconds)
    ->setTimezone(new DateTimeZone('UTC'));

echo $dt->format(DateTimeInterface::ATOM);
// → "2026-03-28T20:00:00+00:00"

// Format in a specific timezone
$dtLocal = $dt->setTimezone(new DateTimeZone('America/New_York'));
echo $dtLocal->format('Y-m-d H:i:s T');
// → "2026-03-28 16:00:00 EDT"

Main gotcha: date()uses the server's default timezone unless you explicitly set it. Always use DateTimeImmutable with an explicit timezone to avoid environment-dependent output.

PostgreSQL

PostgreSQL handles timestamps natively with timestamptz. You can extract epoch values for interoperability and convert back when needed.

-- Current Unix seconds (returns double precision)
SELECT EXTRACT(EPOCH FROM NOW());

-- Convert a timestamptz column to epoch seconds
SELECT EXTRACT(EPOCH FROM created_at) AS epoch_seconds
FROM events;

-- Convert epoch seconds back to timestamptz
SELECT to_timestamp(1700000000);
-- → "2023-11-14 22:13:20+00"

-- Convert epoch milliseconds back to timestamptz
SELECT to_timestamp(1700000000000 / 1000.0);

Main gotcha: EXTRACT(EPOCH ...) returns a double precision value, not an integer. Cast or floor it if you need an exact integer for an API response.

For most relational work, prefer timestamptzcolumns over storing raw epoch integers. PostgreSQL's native type supports indexing, range queries, and date arithmetic natively — integer epoch columns require casting for all of those operations.

MySQL

MySQL provides dedicated functions for epoch conversion. Be aware that UNIX_TIMESTAMP() is affected by the session timezone.

-- Current Unix seconds
SELECT UNIX_TIMESTAMP();

-- Convert a DATETIME column to epoch seconds
SELECT UNIX_TIMESTAMP(created_at) AS epoch_seconds
FROM events;

-- Convert epoch seconds back to a readable datetime
SELECT FROM_UNIXTIME(1700000000);
-- → "2023-11-14 22:13:20"

-- Ensure session is in UTC for consistent results
SET time_zone = '+00:00';
SELECT UNIX_TIMESTAMP(NOW());

Main gotcha: UNIX_TIMESTAMP() interprets its argument in the current session timezone. If your session is set to a local timezone, passing a DATETIME value will convert relative to that zone, not UTC. Set the session timezone explicitly when consistency matters.

Unix timestamps vs ISO 8601 vs database timestamp types

These three formats serve different layers. None of them is universally “correct” — each fits specific needs.

FormatBest forLimitations
Unix timestampMachine-oriented transport, logs, JWT claims, cache TTLs, fast numeric comparison.Not human-readable. Requires unit agreement (s vs ms). Awkward for date arithmetic in SQL.
ISO 8601Human-readable API payloads, interoperability, clear timezone offsets, log readability.Larger payload size. Requires parsing. Timezone offset format varies across implementations.
Native DB timestampRelational queries, indexing, date math, range queries, analytics, reporting.Tied to database engine semantics. Requires timezone-aware types (e.g. timestamptz) for safe cross-zone use.

A practical pattern: use Unix timestamps or ISO 8601 at the transport layer (APIs, message queues, JWT claims), native timestamp types at the storage layer(database columns), and locale-formatted strings at the presentation layer (UI, reports, emails). Converting between formats should happen at well-defined boundaries, not scattered throughout business logic.

Architectural note: the 2038 problem

The Year 2038 problem affects systems that store Unix timestamps in a 32-bit signed integer. The maximum value — 2147483647— corresponds to January 19, 2038 at 03:14:07 UTC. After that, the value overflows.

Most modern systems already avoid this. 64-bit operating systems, modern language runtimes, and current database engines all use 64-bit time representations by default. The risk is real in:

  • Legacy software on 32-bit embedded systems or old kernels.
  • Database schemas using INT (32-bit) columns to store epoch seconds.
  • Serialisation formats that specify 32-bit integer fields for timestamps.

Practical takeaway: if you store epoch integers in a database column, use BIGINT instead of INT. Or use native timestamp types, which handle this transparently. Do not assume that every modern stack is automatically immune — check the actual column type and serialisation format in your schema.

Debugging timestamps in practice

Real debugging scenarios where a quick converter saves time:

  • You see 1774734420 in a JSON payload and need to know what date it represents.
  • You are not sure if 1700000000000is milliseconds or seconds — and the API documentation does not specify.
  • A JWT token expires “soon,” but you need a human-readable check before deciding whether to refresh it.
  • You want to compare UTC output with your local browser time to verify timezone handling in a frontend component.

Do not guess or write throwaway scripts to inspect a timestamp. Use the CodeAva Unix Timestamp Converter to auto-detect seconds vs milliseconds, compare UTC and local time, and inspect JWT time claims instantly.

Conclusion and practical rules

If you take one pattern from this article, let it be this:

  • Use Unix timestamps when you need machine-friendly, timezone-stable time values across systems.
  • Be explicit about seconds vs milliseconds at every system boundary. Document the unit in your API contracts.
  • Use native timestamp types where they make querying and reporting easier. Not every column needs to be an integer.
  • Format for humans at the presentation layeror at clearly defined output boundaries — not in transport or storage.

Need to validate a timestamp fast? Use the CodeAva Unix Timestamp Converter.

Want cleaner payload and logic checks across your app? Explore CodeAva's Website Audit for structural validation and the Code Audit for code-level quality checks.

#unix-timestamp#javascript#python#go#php#sql#epoch-time#date-handling#cheat-sheet

Frequently asked questions

Found this useful?

Share:

Want to audit your own project?

These articles are written by the same engineers who built CodeAva\u2019s audit engine.