Timestamp converter · Guide

Timestamp Seconds vs Milliseconds: Identify 10- and 13-Digit Epoch Values

Unix timestamps commonly count either seconds or milliseconds from the Unix epoch. The value carries no unit marker, so sending a ten-digit second value to an API that expects milliseconds produces a valid but wrong date near 1970; making the opposite mistake can overflow or jump tens of thousands of years ahead. Use the producer's contract first, digit count as a diagnostic, and a range check before trusting the converted result.

The difference is a factor of 1,000

A seconds timestamp advances once per second. A milliseconds timestamp advances 1,000 times per second and preserves three decimal places of sub-second precision. Both use the same epoch, 1970-01-01T00:00:00Z; only the unit differs.

Text
1704067200       seconds      -> 2024-01-01T00:00:00Z
1704067200000    milliseconds -> 2024-01-01T00:00:00.000Z

milliseconds = seconds * 1000
seconds = floor(milliseconds / 1000)

Dividing milliseconds discards the remainder unless you preserve it separately. 1704067200123 milliseconds is 1704067200 whole seconds plus 123 milliseconds. Decide whether truncation, rounding or retained fractional seconds matches the receiving system.

A timestamp represents an instant in UTC rather than a local time. Unit conversion must not add a time-zone offset; apply a zone only when formatting calendar fields for display.

Use digit count as a clue, not a contract

Contemporary positive epoch seconds normally have 10 digits, while milliseconds have 13. This makes 10 vs 13 digit timestamp a useful first check for modern application data. It is not universally correct: dates before September 2001 have nine-digit seconds, negative dates include a sign, and microseconds or nanoseconds use more digits.

Text
value          likely unit near the present
1704067200     seconds       (10 digits)
1704067200000  milliseconds  (13 digits)
1704067200000000 microseconds
1704067200000000000 nanoseconds

Magnitude heuristics become ambiguous for historical values and long-running counters. Prefer a schema field such as created_at_ms, an OpenAPI description saying Unix milliseconds, or an ISO 8601 string. If none exists, compare a sample with a known event time and document the inference.

Strip neither decimal points nor leading signs merely to count digits. A seconds value may legitimately contain a fraction, and changing its text before classification can change the represented instant.

Recognise the silent mismatch symptoms

JavaScript's Date constructor expects milliseconds. Passing current epoch seconds directly yields a date around January 1970 without throwing. That plausible parse is more dangerous than an exception because it can be stored and propagated as valid data.

JavaScript
new Date(1704067200).toISOString();
// 1970-01-20T17:21:07.200Z  wrong unit

new Date(1704067200 * 1000).toISOString();
// 2024-01-01T00:00:00.000Z correct

Treating milliseconds as seconds usually produces an out-of-range year or an exception, depending on the runtime. Some databases clamp, overflow or reject the result. Do not rely on the failure mode; validate that timestamps fall inside a domain-specific interval before conversion.

A sensible interval is contextual. Birth dates may be decades old, telemetry may be only minutes old, and scheduled events can be years ahead. A generic 'after 1970' check will not catch a milliseconds-as-seconds error reliably.

Convert explicitly in JavaScript, Python and PHP

Name variables after their unit and keep conversion at a boundary. Once the application chooses an internal unit, repeated guessing in different functions creates double multiplication and division bugs.

JavaScript
function secondsToIso(epochSeconds) {
  if (!Number.isFinite(epochSeconds)) throw new TypeError('numeric seconds required');
  return new Date(epochSeconds * 1000).toISOString();
}

console.log(secondsToIso(1704067200));
Python
from datetime import datetime, timezone

epoch_ms = 1704067200123
instant = datetime.fromtimestamp(epoch_ms / 1000, tz=timezone.utc)
print(instant.isoformat())
# 2024-01-01T00:00:00.123000+00:00
PHP
$epochMs = 1704067200123;
$seconds = intdiv($epochMs, 1000);
$milliseconds = $epochMs % 1000;
$date = (new DateTimeImmutable('@' . $seconds))->setTimezone(new DateTimeZone('UTC'));
printf('%s.%03dZ', $date->format('Y-m-d\TH:i:s'), $milliseconds);

Avoid an intermediate floating-point number when exact ordering or signatures matter. Split an integer millisecond value into quotient and remainder, or use a date library that accepts the unit explicitly.

Do not confuse microseconds and nanoseconds

Databases and tracing systems often use microseconds or nanoseconds. Repeatedly dividing by 1,000 moves from nanoseconds to microseconds to milliseconds to seconds, but each integer division can discard precision. Record the original unit alongside the value.

JavaScript
const epochNanoseconds = 1704067200123456789n;
const epochMilliseconds = epochNanoseconds / 1_000_000n;
const remainderNanoseconds = epochNanoseconds % 1_000_000n;
// Keep the remainder if a later round trip must be exact.

Current epoch nanoseconds exceed JavaScript's safe-integer range. Parsing them as a Number silently changes low-order digits before conversion. Use BigInt or retain decimal text; JSON itself has no bigint type, so APIs frequently transmit such values as strings.

Precision is different from accuracy. A source can provide nine fractional digits even when its clock is accurate only to milliseconds. Do not infer measurement quality merely from the number of digits.

Validate and normalise at API boundaries

Reject a field whose unit is undocumented rather than guessing differently in each client. Schemas should specify integer or decimal, unit, accepted range and whether negative values are permitted. Separate fields are clearer than an overloaded timestamp property.

Text
{
  "created_at_ms": 1704067200123,
  "created_at": "2024-01-01T00:00:00.123Z"
}

# Either representation is clear; a bare "timestamp" is not.

When integrating a legacy API, convert once in the adapter and expose a typed instant internally. Add tests around a known timestamp with non-zero milliseconds so a mistaken truncation cannot pass unnoticed.

If numeric timestamps arrive as strings, validate the whole string before conversion. Functions such as parseInt may accept trailing junk, while implicit coercion can turn an empty string into zero—the Unix epoch—without signalling bad input.

A reliable debugging checklist

Find the producing system and its documented unit. Count digits only as supporting evidence. Convert a known sample in UTC, compare it with the event's real time, inspect sub-second precision, and apply a realistic range check. Then trace where multiplication or division occurs so it happens exactly once.

Text
input: 1704067200123
contract: Unix milliseconds
quotient: 1704067200 seconds
remainder: 123 milliseconds
UTC result: 2024-01-01T00:00:00.123Z
range check: valid for application data

Log the original value and unit during diagnosis, not only the formatted date. Once a bad conversion becomes 1970-01-20, the original scale is harder to reconstruct. Avoid logging timestamps that indirectly disclose sensitive user events unless operationally necessary.

Round-trip tests are valuable: convert the input to an instant and back to the declared unit. Equality confirms the unit and precision path, except where you intentionally truncated a remainder.

Frequently asked questions

How can I tell whether a timestamp is seconds or milliseconds?

For dates near the present, Unix seconds usually have 10 digits and milliseconds 13. Treat that as a clue only; confirm the producer's contract or compare a known event because historical, negative and higher-precision timestamps break the rule.

Why does my timestamp convert to 1970?

A seconds value was probably passed to an API expecting milliseconds. JavaScript Date expects milliseconds, so current epoch seconds must be multiplied by 1,000 before construction.

How do I convert milliseconds to seconds?

Divide by 1,000. Use integer division when whole seconds are required, and preserve the remainder separately if millisecond precision or an exact round trip matters.

Is a 13-digit timestamp always milliseconds?

It is normally milliseconds for contemporary Unix dates, but digit count alone is not a formal type. Domain-specific epochs, counters and far-future data can have the same length, so confirm the schema.

Can JavaScript store millisecond timestamps safely?

Yes for ordinary application dates because they remain below Number.MAX_SAFE_INTEGER. Current microsecond and nanosecond epoch values do not; use BigInt, strings or a precision-aware library for them.

Does converting a timestamp change its time zone?

No. An epoch timestamp identifies an absolute instant and has no local zone. A time zone affects only how that instant is formatted as calendar fields.

Ready to try it?

Open the free browser-based Timestamp converter and apply what you just read — no sign-up, runs locally.

Open the Timestamp converter tool