Guides unix timestamp epoch time milliseconds developer date conversion

Unix Timestamp in Seconds vs Milliseconds: How to Tell and Convert (Every Language)

By ClickSolveTools Editorial Team10 min read

A Unix timestamp with 10 digits is in seconds, 13 digits is milliseconds, 16 is microseconds and 19 is nanoseconds. That digit pattern holds for every date from September 9, 2001 to November 20, 2286. All four numbers in the table below name the same instant, 2026-09-26T00:00:00Z, and all count from 1970-01-01T00:00:00Z. Only the unit differs.

If a converter shows a date in January 1970, you gave it seconds when it wanted milliseconds. If it shows a year in the tens of thousands (58,704 for this example), an error or NULL, you gave it milliseconds when it wanted seconds. To check one value, paste it into the Unix Timestamp Converter, which reads the unit from the size of the number and tells you what it picked.

Quick Answer: Four Units, One Instant

Unit Digits Value for 2026-09-26T00:00:00Z Where you meet it
Seconds 10 1790380800 Unix tools, PHP time(), MySQL UNIX_TIMESTAMP(), JWT exp and iat
Milliseconds 13 1790380800000 JavaScript Date.now(), Java System.currentTimeMillis()
Microseconds 16 1790380800000000 Go UnixMicro(), Google Sheets EPOCHTODATE unit 3
Nanoseconds 19 1790380800000000000 Go UnixNano(), Python time.time_ns(), GNU date +%s%N

What a Unix Timestamp Counts

A Unix timestamp is the number of seconds since 1970-01-01T00:00:00 UTC, called the epoch. It has no time zone. The value 1790380800 is the same instant in Tokyo and in Phoenix, and only the calendar text you print from it differs. The other units are the same count scaled: multiply by 1,000 for milliseconds, 1,000,000 for microseconds and 1,000,000,000 for nanoseconds.

POSIX defines "Seconds Since the Epoch" so that every day is exactly 86,400 seconds, which means leap seconds are not counted. You can check it with our example: 1,790,380,800 / 86,400 = 20,722 days exactly, and 20,722 days after 1970-01-01 is September 26, 2026 at midnight UTC.

How to Tell Which Unit You Have

The digit rule

Count the digits. It works because 1,000,000,000 seconds after the epoch is 2001-09-09T01:46:40Z and 9,999,999,999 is 2286-11-20T17:46:39Z, so every date in that window has 10 digits in seconds and 13 in milliseconds. Outside the window the count shifts:

The magnitude rule

In code, compare the absolute value with fixed cutoffs instead of counting characters. This is the same rule our converter uses:

function unitOf(n) { const a = Math.abs(Number(n)); if (a < 1e11) return 's'; // seconds, up to the year 5138 if (a < 1e14) return 'ms'; // milliseconds, 1973-03-03 and later if (a < 1e17) return 'us'; // microseconds return 'ns'; // nanoseconds }

The cutoffs work because 1e11 read as milliseconds is 1973-03-03T09:46:40Z, while 1e11 read as seconds is 5138-11-16T09:46:40Z. The rule assumes your dates fall between those two. It cannot separate a millisecond value from before March 1973 from a seconds value, so force the unit if your data is that old.

Check the source's contract

Guessing is the fallback. Better to read what produced the number. JavaScript's Date.now() and Java's System.currentTimeMillis() return milliseconds. The JWT exp, nbf and iat claims are seconds (RFC 7519). Go and Python give you one method per unit. The first 48 bits of a version 7 UUID, its first 12 hex digits, are a millisecond timestamp (RFC 9562), and the UUID Generator can make one.

Get the Current Timestamp in 10 Languages and Shells

Language Seconds Milliseconds Notes
JavaScript Math.floor(Date.now() / 1000) Date.now() A whole number of milliseconds; a Date cannot hold anything finer
Python int(time.time()) time.time_ns() // 1_000_000 time.time() is float seconds; time.time_ns() (3.7+) is an integer
Java Instant.now().getEpochSecond() System.currentTimeMillis() System.nanoTime() has an arbitrary origin and is not a timestamp
Go time.Now().Unix() time.Now().UnixMilli() UnixMilli() and UnixMicro() need Go 1.17; UnixNano() is undefined before 1678 and after 2262
PHP time() (int) floor(microtime(true) * 1000) microtime(true) is float seconds, accurate to the microsecond
C# DateTimeOffset.UtcNow.ToUnixTimeSeconds() DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() Both return a long and ignore leap seconds
Bash (GNU date) date +%s date +%s%3N %N is nanoseconds and %3N keeps three digits. macOS date reportedly gained %N only in macOS 15, so older Macs need gdate from coreutils
PostgreSQL floor(EXTRACT(EPOCH FROM now())) floor(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) to_timestamp(x) converts back, and x must be seconds. now() stays fixed for the whole transaction, while clock_timestamp() is the live clock
MySQL UNIX_TIMESTAMP() CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED) FROM_UNIXTIME() shows the result in the session time zone
SQLite unixepoch() CAST(unixepoch('subsec') * 1000 AS INTEGER) unixepoch() needs SQLite 3.38.0 (2022) and 'subsec' needs 3.42.0 (2023)

Convert Between Units Without Precision Bugs

Use integer division. In Python, 1790380800123 // 1000 is the integer 1790380800, while / returns the float 1790380800.123. For timestamps before 1970, round down, not toward zero. The value -1500 ms is 1969-12-31T23:59:58.500Z, which falls in second -2. Math.floor(-1500 / 1000) and Python's -1500 // 1000 both give -2, but Math.trunc gives -1.

Watch float limits. A JavaScript number is a double, and Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991 (253 - 1). Milliseconds are always safe inside the range of Date. Microseconds stay exact until 2255-06-05. A nanosecond timestamp passes the limit after 1970-04-15, so 1790380800123456789 becomes 1790380800123456768 the moment it is parsed as a Number, which is 21 ns off (JavaScript prints that value as 1790380800123456800). JSON.parse does this silently, so keep 19-digit values as strings or BigInt:

const ns = BigInt('1790380800123456789'); new Date(Number(ns / 1_000_000n)).toISOString(); // "2026-09-26T00:00:00.123Z"

Python's time.time() has the same problem in float form: 1790380800.123456789 comes back as 1790380800.1234567, which is why the Python docs point you to time_ns(). Our JSON formatting guide covers the same big-integer trap for JSON APIs.

Common Bugs and What They Mean

Symptom Cause Fix
A date in January 1970. 1790380800 shows 1970-01-21T17:19:40.800Z, about 20.7 days after the epoch Seconds passed to something that expects milliseconds, such as new Date() or Java's Instant.ofEpochMilli Multiply by 1,000
The year 58,704, an exception, NULL, 0 or ##### in Excel. 1790380800000 read as seconds is 58704-11-29 Milliseconds passed to something that expects seconds, such as Python's fromtimestamp, FROM_UNIXTIME or Excel's /86400 Divide by 1,000
Times wrong by whole hours A zone offset added to the timestamp by hand, then formatted in local time as well Never shift a timestamp. Convert zones only when you format

How the "too big" case fails depends on the tool. SQLite returns NULL, MySQL's FROM_UNIXTIME returns 0 above 32,536,771,199, GNU date -d @1790380800000 prints a year-58704 date, and JavaScript gives Invalid Date once the value passes 8,640,000,000,000,000 ms.

The double shift is easy to reproduce. In Phoenix (UTC-7, no daylight saving), 1790380800 is September 25, 2026 at 5:00 PM. Subtract 25,200 seconds yourself before formatting and it shows 10:00 AM, seven hours too early.

Date strings cause a related bug. JavaScript reads a date-time without Z or an offset, such as 2026-09-26T00:00:00, as local time, but a date-only string such as 2026-09-26 as UTC (MDN). Python's naive datetime.timestamp() also assumes local time. In a UTC-7 zone the first string gives 1790406000, which is 25,200 seconds more than the 1790380800 you get from 2026-09-26T00:00:00Z. Always send Z or an offset, and compare zones with the Time Zone Converter. Scheduled jobs add their own wall-clock rules, which the Cron Expression Parser and our cron expressions guide help you check.

Excel and Google Sheets

Excel stores dates as day counts, where January 1, 1900 is serial number 1. DATE(1970,1,1) is 25,569, so you divide the timestamp into days and add that. With the timestamp in A1:

=A1/86400+DATE(1970,1,1) seconds =A1/86400000+DATE(1970,1,1) milliseconds

Format the cell as a date and time, for example yyyy-mm-dd hh:mm:ss. For 1790380800: 1,790,380,800 / 86,400 = 20,722, and 20,722 + 25,569 = 46,291, which is 2026-09-26 00:00:00. The result is UTC. For local time, add your offset in days, such as -7/24 for UTC-7.

Excel keeps only 15 significant digits, so 16-digit and 19-digit values cannot be stored exactly as numbers. Keep them as text and read the millisecond part of any date from September 9, 2001 on with this:

=VALUE(LEFT(A1,13))/86400000+DATE(1970,1,1)

Google Sheets has a built-in function: =EPOCHTODATE(A1) for seconds, =EPOCHTODATE(A1, 2) for milliseconds and =EPOCHTODATE(A1, 3) for microseconds. Like Excel's formula, it returns UTC rather than your spreadsheet's time zone.

Convert a Whole Column at Once

Paste the list into the Bulk Unix Timestamp Converter. It detects seconds, milliseconds, microseconds or nanoseconds line by line, so a column that mixes units still converts, and it exports the results as CSV. Its arithmetic uses exact integers, so 19-digit nanosecond values are not rounded.

The Year 2038 Problem

A signed 32-bit integer tops out at 2,147,483,647, which is 2038-01-19T03:14:07Z. One second later it wraps to -2,147,483,648, which is 1901-12-13T20:45:52Z. An unsigned 32-bit value lasts until 4,294,967,295, or 2106-02-07T06:28:15Z, and a signed 64-bit count of seconds lasts about 292 billion years.

Most current systems already use 64 bits. Debian 13 uses a 64-bit time_t on every architecture except i386, and MySQL on 64-bit platforms accepts FROM_UNIXTIME values up to 32,536,771,199 (3001-01-18 23:59:59 UTC) where 32-bit platforms stop in 2038. MySQL's TIMESTAMP column type is the exception: it still ends at 2038-01-19 03:14:07 UTC on every platform, so use DATETIME or BIGINT for later dates. The risk sits in old embedded devices, legacy C code and database columns declared as 32-bit integers or MySQL TIMESTAMP. A 13-digit millisecond timestamp does not fit in 32 bits at all, so store it as BIGINT or long. To count the days left until a date such as 2038-01-19, use the Days Between Dates Calculator.

Frequently Asked Questions

Is a Unix timestamp always UTC?

Yes, in the way that matters. A Unix timestamp counts seconds from 1970-01-01T00:00:00Z, so it names one instant and is the same in every time zone. It carries no zone of its own. A zone only matters when you turn the number into calendar text, or calendar text into a number, so convert at those edges and leave the timestamp untouched in between.

How do I convert a Unix timestamp in Excel or Google Sheets?

In Excel, with the timestamp in A1, use =A1/86400+DATE(1970,1,1) for seconds or =A1/86400000+DATE(1970,1,1) for milliseconds, then format the cell as a date and time. In Google Sheets, use =EPOCHTODATE(A1, 2) for milliseconds. The second argument is 1 for seconds (the default), 2 for milliseconds and 3 for microseconds. Both return UTC.

Why does my timestamp show a date in 1970?

Your code read a seconds value as milliseconds. A value such as 1790380800 treated as milliseconds is only about 20.7 days after the epoch, which is 1970-01-21T17:19:40.800Z. Multiply the seconds by 1,000 before you pass them to something that expects milliseconds, such as the JavaScript Date constructor. The opposite mistake, milliseconds read as seconds, lands around the year 58,704.

How many digits is a millisecond timestamp?

Thirteen digits for any date from 2001-09-09 to 2286-11-20, for example 1790380800000. Dates from 1973-03-03 up to that point have 12 digits, so the digit count is a hint, not a rule. In code, compare the size of the number instead: below 100 billion is seconds, below 100 trillion is milliseconds, below 100 quadrillion is microseconds, and anything larger is nanoseconds.

Does Unix time count leap seconds?

No. POSIX defines seconds since the Epoch so that every day is exactly 86,400 seconds, and the Python and .NET documentation both say leap seconds are not counted. That is why 1790380800 divided by 86,400 is exactly 20,722 days. How a system clock handles a real leap second is implementation-defined, so the gap between two timestamps can be off by a second for each leap second in between.

Sources

Figures and rules in this guide were checked against these primary references in 2026: