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.
| 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.
| 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 |
| 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 |
| 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')` |
| 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.
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.
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.
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.
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.
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.
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.
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.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().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.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.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.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.date and Python on Linux handled -1 and -86400 above, but test Windows and your database before relying on it.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.