Cheatsheet

Unix Timestamps Cheatsheet

A Unix timestamp counts the seconds since 1970-01-01T00:00:00Z, so the same number means the same instant in every time zone. This sheet is for developers who convert timestamps between languages, databases and logs. The confusion it clears up: APIs mix seconds, milliseconds and nanoseconds, and a value that is 1,000 times off lands in the year 58730 instead of 2026. See the Unix timestamp glossary entry for the definition.

Quick reference

Units and digit counts

Unit Digits today Example for 2026-10-05T12:00:00Z Typical source
Seconds 10 `1791201600` `time()` in PHP, `date +%s`, JWT `exp`, `git log %ct`, SQLite `unixepoch()`
Milliseconds 13 `1791201600000` JavaScript `Date.now()`, `date +%s%3N`
Microseconds 16 `1791201600000000` Python `time.time_ns() // 1000`
Nanoseconds 19 `1791201600000000000` `date +%s%N`, Python `time.time_ns()`

Ten-digit second values run from 2001-09-09 (1000000000) up to 9999999999, which is 2286-11-20T17:46:39Z.

Well-known values

Timestamp UTC date and time Why it matters
`0` 1970-01-01T00:00:00Z The epoch
`86400` 1970-01-02T00:00:00Z One day, always exactly 86,400 seconds
`946684800` 2000-01-01T00:00:00Z Y2K
`1000000000` 2001-09-09T01:46:40Z One billion seconds
`1234567890` 2009-02-13T23:31:30Z Popular test value
`1700000000` 2023-11-14T22:13:20Z Common fixture in docs
`1767225600` 2026-01-01T00:00:00Z Start of 2026
`1798761600` 2027-01-01T00:00:00Z Start of 2027
`2000000000` 2033-05-18T03:33:20Z Two billion seconds
`2147483647` 2038-01-19T03:14:07Z Largest signed 32-bit value
`4294967295` 2106-02-07T06:28:15Z Largest unsigned 32-bit value
`-1` 1969-12-31T23:59:59Z One second before the epoch
`-2147483648` 1901-12-13T20:45:52Z Smallest signed 32-bit value

Get the current timestamp

Language Seconds Milliseconds
JavaScript `Math.floor(Date.now() / 1000)` `Date.now()`
Python `int(time.time())` `int(time.time() * 1000)`
PHP `time()` `(int) (microtime(true) * 1000)`
Shell (GNU date) `date +%s` `date +%s%3N`
SQLite `unixepoch()` `unixepoch('subsec') * 1000`
Git `git log -1 --format=%ct` not available

Convert a timestamp to a date and back

Language Timestamp to date Date to timestamp
JavaScript `new Date(ts * 1000).toISOString()` `Date.parse("2026-10-05T12:00:00Z") / 1000`
Python `datetime.fromtimestamp(ts, timezone.utc)` `calendar.timegm(tuple)` or an aware `.timestamp()`
PHP `gmdate("c", $ts)` `gmmktime(12, 0, 0, 10, 5, 2026)`
Shell (GNU date) `date -u -d @$ts +%FT%TZ` `date -u -d '2026-10-05 12:00:00' +%s`
SQLite `datetime(ts, 'unixepoch')` `unixepoch('2026-10-05 12:00:00')`

Arithmetic

Task Formula
Add one day `ts + 86400`
Start of the UTC day `ts - ts % 86400`
Days since the epoch `ts // 86400`, so 2026-10-05 is day `20731`
Seconds to milliseconds multiply by `1000`
Milliseconds to seconds divide by `1000` and floor

The POSIX definition of seconds since the Epoch counts every day as exactly 86,400 seconds. That makes the arithmetic above exact and also means leap seconds are not counted.

Common patterns

Convert a timestamp with a known value

date -u -d @1791201600 +%Y-%m-%dT%H:%M:%SZ
node -e 'console.log(new Date(1791201600 * 1000).toISOString())'
php -r 'echo gmdate("c", 1791201600), "\n";'
2026-10-05T12:00:00Z
2026-10-05T12:00:00.000Z
2026-10-05T12:00:00+00:00

All three read the same instant. GNU date takes the @ prefix for epoch seconds, JavaScript wants milliseconds, and PHP gmdate formats in UTC.

Detect seconds versus milliseconds

const toMs = (n) => (Math.abs(n) < 1e11 ? n * 1000 : n);
for (const n of [1791201600, 1791201600000, -86400]) {
  console.log(n, toMs(n), new Date(toMs(n)).toISOString());
}
1791201600 1791201600000 2026-10-05T12:00:00.000Z
1791201600000 1791201600000 2026-10-05T12:00:00.000Z
-86400 -86400000 1969-12-31T00:00:00.000Z

Below 1e11 the value is read as seconds, which covers dates up to 5138-11-16. This guess is only safe for modern dates, so fix the unit in your API contract rather than guessing in production.

Turn an ISO 8601 string with an offset into a timestamp

from datetime import datetime
print(int(datetime.fromisoformat("2026-10-05T09:30:00+02:00").timestamp()))
1791185400

JavaScript's Date.parse("2026-10-05T09:30:00+02:00") / 1000 gives the same 1791185400. Always keep the offset or a Z in the string. The offset is subtracted, so 09:30 at +02:00 is 07:30Z.

Find the start and end of a UTC day

ts = 1791201600
day_start = ts - ts % 86400
print(day_start, day_start + 86400, ts // 86400)
1791158400 1791244800 20731

day_start is 2026-10-05T00:00:00Z. This works only in UTC. For a local day in a daylight saving zone, use the time zone library, because that day can have 23 or 25 hours.

Query timestamps in SQLite

import sqlite3
db = sqlite3.connect(":memory:")
print(db.execute("select unixepoch('2026-10-05 12:00:00'), datetime(1791201600, 'unixepoch'), unixepoch('2026-10-05 12:00:00', '+1 day') - unixepoch('2026-10-05 12:00:00')").fetchone())
print(db.execute("select datetime(1791201600123 / 1000, 'unixepoch')").fetchone())
(1791201600, '2026-10-05 12:00:00', 86400)
('2026-10-05 12:00:00',)

SQLite 3.45.1 ran this. The unixepoch() function exists from SQLite 3.38, and datetime(x, 'unixepoch') reads seconds, so divide millisecond columns by 1000 first.

Set a Git commit to a fixed timestamp

GIT_AUTHOR_DATE="@1791201600 +0000" GIT_COMMITTER_DATE="@1791201600 +0000" git commit -m t
git log -1 --format='%at %ct %aI'
1791201600 1791201600 2026-10-05T12:00:00+00:00

The @seconds +offset form is Git's raw date format. %at is the author time, %ct the committer time and %aI the author date in strict ISO 8601. Fixed dates make builds and test repositories reproducible.

Pitfalls

  • Milliseconds read as seconds: passing a 13-digit JavaScript value to a seconds API gives absurd dates. GNU date -u -d @1791201600123 prints Wed Dec 3 00:02:03 UTC 58730, and Python raises ValueError: year 58730 is out of range on Linux (an OSError on Windows). Count the digits first.
  • Naive local time becomes the wrong instant: datetime(2026, 10, 5, 12, 0).timestamp() in Python returned 1791201600 with TZ=UTC, 1791216000 with TZ=America/New_York and 1791169200 with TZ=Asia/Tokyo. Attach timezone.utc or a zone before you call .timestamp().
  • The 2038 overflow: a signed 32-bit seconds counter ends at 2147483647, which is 2038-01-19T03:14:07Z, and wraps to -2147483648 (1901-12-13T20:45:52Z) one second later. Check columns, binary formats and C time_t on 32-bit systems. MySQL's TIMESTAMP type is documented with an upper limit of 2038-01-19 03:14:07 UTC.
  • JavaScript loses nanosecond digits: Number(1791201600123456789n) prints 1791201600123456800, because doubles hold about 15 to 17 significant digits. Keep nanoseconds in a BigInt or as a string and divide by 1000000n to get milliseconds.
  • Leap seconds do not exist in Unix time: 2016-12-31T23:59:59Z is 1483228799 and 2017-01-01T00:00:00Z is 1483228800, one second apart even though a leap second was inserted between them. Use it for ordering and display, not for astronomical precision.
  • Timestamps carry no time zone: 1791201600 is 14:00 in Berlin and 21:00 in Tokyo, and only a zone turns it into a wall-clock time. Store the number and the zone name separately when the local wall-clock time matters, and see the Time Zone List Cheatsheet for zone names.
  • Negative values are not portable: POSIX leaves the relationship undefined for dates before 1970, and some languages reject them. JavaScript, GNU date and Python on Linux handled -1 and -86400 above, but test Windows and your database before relying on it.
  • JWT times are seconds: the exp, iat and nbf claims are NumericDate values in seconds, so a JavaScript Date.now() value there makes a token valid for tens of thousands of years.

Related ZipKit tools

Related cheatsheets

  • Time Zone List Cheatsheet — IANA zone names and offsets for turning one instant into local time
  • JavaScript Date Cheatsheet — the Date object that stores a timestamp in milliseconds
  • Cron Syntax Reference — scheduling expressions, which run in a time zone rather than on raw timestamps