A Unix timestamp is the number of seconds that have passed since 00:00:00 UTC on 1 January 1970, the epoch. Many systems count in smaller units instead, and the quickest way to tell them apart is the digit count of a present-day value: 10 digits is seconds, 13 is milliseconds, 16 is microseconds and 19 is nanoseconds. Read a millisecond value as seconds and you get a year around 55,000; read seconds as milliseconds and you get January 1970. This article explains the epoch, the units and the digit rule with its limits, shows why a timestamp has no time zone while its date does, covers ISO 8601, and ends with the year 2038 problem.
What the epoch is
1700000000 is the moment 22:13:20 UTC on 14 November 2023. Zero is the epoch, 1 January 1970 00:00:00 UTC, and negative numbers are earlier: -86400 is 31 December 1969. A timestamp is a single number for a single moment in the world, the same in every time zone, which is why databases, logs, APIs and file systems like it.
Unix time ignores leap seconds. It counts every day as exactly 86,400 seconds, so one timestamp corresponds to exactly one date and the other way round.
Seconds, milliseconds, microseconds, nanoseconds
The same moment as written by different systems, using 14 November 2023:
| Unit | Value | Digits | Where you meet it |
|---|---|---|---|
| Seconds | 1700000000 | 10 | Unix date +%s, PHP time(), JWT claims, many databases |
| Milliseconds | 1700000000123 | 13 | JavaScript Date.now(), Java System.currentTimeMillis() |
| Microseconds | 1700000000123456 | 16 | Some databases and tracing systems |
| Nanoseconds | 1700000000123456789 | 19 | Go UnixNano(), Python time.time_ns() |
At the time of writing, the current time is about 1791895973 in seconds and 1791895973607 in milliseconds, so the 10 and 13 digit counts are what you will actually see. Python's time.time() returns seconds with a fraction, as in 1700000000.5.
The digit rule, and where it stops working
Count the digits of the whole part. The converter below reads up to 11 digits as seconds, 12 to 14 as milliseconds, 15 to 17 as microseconds and 18 or more as nanoseconds. Those cut-offs are chosen so that the rule is right for dates from about 1973 to the year 2286:
- Ten-digit seconds run from
1000000000(9 September 2001) to9999999999(20 November 2286). Eleven digits stretch it to the year 5138. - Thirteen-digit milliseconds run from
1000000000000(also 9 September 2001) on. Twelve-digit milliseconds go back to100000000000, which is 3 March 1973.
The rule fails for old dates. A millisecond timestamp from 1972 has 11 digits, and a tool that goes by digit count reads it as seconds in the year 5138. Likewise a seconds timestamp before September 2001 has only 9 digits. If you work with historical data, choose the unit yourself instead of trusting auto-detection.
The two classic mistakes are symmetrical:
| What you did | Result for 1700000000 or 1700000000000 |
|---|---|
| Read milliseconds as seconds | 1700000000000 seconds is about the year 55,841 |
| Read seconds as milliseconds | 1700000000 ms is 20 January 1970, 16:13:20 UTC |
Seeing 1970 where you expected a recent date, or a year in the tens of thousands, points to this mistake in one step.
A timestamp has no time zone, a date does
The same 1700000000 shows up as different clock times depending on where you are:
| Zone | Offset | Date and time |
|---|---|---|
| UTC | +00:00 | 14 Nov 2023, 22:13:20 |
| Ho Chi Minh City | +07:00 | 15 Nov 2023, 05:13:20 |
| New York | -05:00 | 14 Nov 2023, 17:13:20 |
| Tokyo | +09:00 | 15 Nov 2023, 07:13:20 |
Note that in Ho Chi Minh City it is already the next day. A bug that reports "the wrong day" is usually a timestamp converted in the wrong zone. Store and exchange the timestamp, or a UTC date, and apply the zone only when you display a value.
ISO 8601 in practice
ISO 8601 is the date format meant to avoid ambiguity: year first, then month, day, T, then the time and the offset. The same moment can be written two ways:
2023-11-14T22:13:20Z
2023-11-15T05:13:20+07:00Z means UTC, and +07:00 means seven hours ahead of UTC. Both are exactly 1700000000. A date-time without a Z or an offset is ambiguous: it names a clock reading, not a moment. JavaScript adds a quirk of its own: new Date("2023-11-15") is read as midnight UTC, while new Date("2023-11-15T00:00:00") is read as midnight in the local time zone. On a computer set to Ho Chi Minh City time, the second one is 17:00 UTC on the 14th. Always write the offset when the date is going to another system.
Daylight saving: times that do not exist
Where clocks change, some clock readings never happen and some happen twice. In New York on 10 March 2024 the clocks jumped from 02:00 to 03:00, so 2024-03-10 02:30 never existed; the converter moves it forward to 03:30 and says so. On 3 November 2024 the clocks went back from 02:00 to 01:00, so 01:30 happened twice, at 05:30 UTC and again at 06:30 UTC; the converter uses the earlier one and says so. The lesson for schedules and logs is to keep the underlying timestamp in UTC. The cron expression cheat sheet shows how the same effect hits scheduled jobs.
The year 2038 problem
A signed 32-bit integer holds values up to 2,147,483,647. As a timestamp in seconds, that is 2038-01-19 03:14:07 UTC. One second later the counter wraps around to -2,147,483,648, which is 13 December 1901, 20:45:52 UTC. Systems that still keep seconds in a 32-bit signed field, such as old embedded devices, some file formats and database columns declared as a 32-bit integer, would then show dates from 1901.
An unsigned 32-bit counter lasts until 7 February 2106, 06:28:15 UTC. A 64-bit counter makes the problem go away for any practical purpose: it overflows about 292 billion years from now. The fix is to use 64-bit time values, and to check the type of every column and field that stores a timestamp. Milliseconds in a 32-bit field overflow much sooner, in less than 25 days after the epoch.
Converting with the tool
- Open the timestamp converter. Type or paste a value in the one box: a number is read as a timestamp, a date is read as a date. With the box empty it shows the current time and updates every second; switch "Live clock" off to freeze it, or press Now.
- Check the note under the box, such as "Read as milliseconds (13 digits)". If it guessed the unit wrong, choose Seconds, Milliseconds, Microseconds or Nanoseconds under "Timestamp unit".
- Pick the Time zone. The default is your device's. A date typed without an offset is read in the chosen zone; one with
Zor an offset such as+07:00carries its own. - Copy any row: Unix seconds, milliseconds, microseconds, nanoseconds, ISO 8601 in UTC and in the zone, RFC 2822, a readable form, and a relative time such as "3 days ago".
It accepts years 1 to 9999 and does all the maths in your browser, using the time zone data your browser already has. A JWT's exp is such a seconds value; how to read a JWT puts it to use. Time-ordered identifiers carry a millisecond timestamp too, as the UUID v4, v7 and ULID comparison shows.
Command-line and code recipes
date +%s # now, in seconds
date -u -r 1700000000 # macOS: seconds to date, in UTC
date -u -d @1700000000 # GNU/Linux: the sameDate.now(); // milliseconds
new Date(1700000000 * 1000).toISOString(); // "2023-11-14T22:13:20.000Z"
Math.floor(Date.now() / 1000); // secondsIn a spreadsheet, =A2/86400+25569 turns a timestamp in seconds into a date once the cell is formatted as a date, because 25569 is the number of days between Excel's day zero and 1970.
A short checklist
Count the digits before you convert, and choose the unit yourself for dates before 1973. Keep stored times as UTC timestamps and apply a time zone only when showing them. Write ISO 8601 with an offset when exchanging dates. Be careful with clock readings around daylight saving changes, and use 64-bit fields for anything that must work after January 2038.