Real Epoch Converter logo realepochconverter
guide

Seconds, Milliseconds, or Microseconds? Detecting a Timestamp’s Unit

A 13-digit value is usually milliseconds and a 10-digit value is usually seconds — but "usually" is not a debugging strategy. This guide explains exactly where the digit boundaries fall, why they move over time, and how to confirm a reading when a value is ambiguous.

Why the same moment has so many digit counts

A timestamp is the elapsed time since the epoch, but every system picks its own scale. Count in seconds and the current moment is a 10-digit number; count in milliseconds and the same instant is 13 digits; in microseconds, 16. Multiply or divide by a thousand at the wrong step and you are suddenly reading December 1973 as 1970, or telling users their session expired 46 years ago. It is the single most common timestamp bug in production.

The digit table for today

Because the count grows by roughly one digit every 11.5 days at the millisecond scale and every 31.7 years at the second scale, the digit counts are stable for long stretches. For values from 1973 until 2286 — which covers every timestamp you will meet in a modern log — the mapping is:

  • 10 digits → seconds. Example: 1722750000 = August 4, 2024 05:40:00 UTC.
  • 13 digits → milliseconds. Example: 1722750000123 = the same instant, plus 123 ms.
  • 16 digits → microseconds. Example: 1722750000123456.
  • 17–19 digits → nanoseconds. Example: 1722750000123456789 = Go's UnixNano() for the same instant. The main epoch converter decodes these directly.

The same three values are shown side by side on every result row of the main epoch converter, precisely so a misread unit is visible at a glance.

Where the boundaries actually are

The simple table hides two wrinkles worth knowing about:

  • Old values have fewer digits. Between March 1973 and September 9, 2001 (Unix time 1,000,000,000), milliseconds were 12 digits and microseconds 15. A 12-digit value in an archive from that era is therefore milliseconds, not seconds — decode it as such.
  • Seconds stay at 10 digits for centuries. Ten digits last until 2286, when seconds roll over to 11 digits. Values between now and then are always 10-digit seconds, which is why the rule is so reliable in practice.

And one genuine trap: 19-digit values are usually nanoseconds (Go's time.Now().UnixNano()). Nanoseconds are not seconds, milliseconds, or microseconds — divide by 1,000 to get microseconds before converting.

Which unit does each system use?

When in doubt, the source of the value usually settles it:

  • Seconds: PHP time(), C time(), MySQL UNIX_TIMESTAMP(), Postgres EXTRACT(EPOCH ...), JWT iat/exp, Ruby Time.now.to_i.
  • Milliseconds: JavaScript Date.now(), Java System.currentTimeMillis(), Go time.Now().UnixMilli(), C# DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), most analytics SDKs.
  • Microseconds: Go time.Now().UnixMicro(), PostgreSQL now() internally (but EXTRACT(EPOCH) returns fractional seconds).
  • Fractional seconds: Python time.time() and Node's Date.now()/1000 — the integer part is seconds.
  • Nanoseconds: Go UnixNano(), Rust SystemTime::now().duration_since(UNIX_EPOCH).

Sanity checks before you trust a reading

A converted date that makes historical sense is the cheapest validation available:

  • The decade check. If the decoded year is 1969–1973 when the log is from this year, you almost certainly divided where you should have multiplied — or read milliseconds as seconds.
  • The "now" check. Run Date.now() (13 digits) or date +%s (10 digits) and compare shapes: a fresh value in the same log should have the same digit count as yours.
  • The ordering check. In a sequence of events, decoded dates must be in the same order as the raw values. A single date that jumps out of sequence is a unit or timezone slip.

When digits are not enough

Digit counting is a heuristic, and heuristics fail at the edges: a pre-2001 millisecond value, a second value logged with a leading zero, or a float whose fractional part changes the count. That is why the converter lets you lock the unit manually with the sec/ms/µs chips next to the input, and why it always renders the result in all three units so a disagreement is impossible to miss. When two readings disagree, trust the one the source system documents — and if the value came from a database column typed as an integer, check the column's documentation before you assume anything at all.

First published · Last reviewed · Maintained and developed by the Real Epoch Converter team · [email protected] · Contact · Methodology

Copied