Timestamp2Date
← Back to blogs
•Unix TimestampMillisecondsTime

Milliseconds vs Seconds Timestamps

Milliseconds vs Seconds Timestamps cover

I once shipped a bug where "expires in 15 minutes" links were expiring basically right away.

The backend logic was fine. The database was fine. The bug was one dumb mismatch:

Frontend sent milliseconds (Date.now()), backend expected seconds.

That was it.

If you have ever seen dates jump to 1970 or some absurd year in the future, you have probably hit the same thing.

The actual difference

Unix time can be stored in different units.

  • seconds: 1719835200
  • milliseconds: 1719835200000

Same moment, different scale.

milliseconds = seconds * 1000

Simple, but easy to miss when data moves between browser code, APIs, background jobs, and SQL.

The fast unit check I use in practice

For modern timestamps, digit count is a great first pass:

  • 10 digits: usually seconds
  • 13 digits: usually milliseconds
  • 16 digits: usually microseconds
  • 19 digits: usually nanoseconds

I also sanity-check magnitude:

  • around 1.7e9 -> likely seconds
  • around 1.7e12 -> likely milliseconds

Not mathematically perfect forever, but very reliable for current production systems.

Where teams usually get burned

Browser to backend

In JavaScript, Date.now() returns milliseconds.

A lot of backend code (especially older Unix-style systems) expects seconds.

So the data is numeric and parseable, but semantically wrong. That is why this slips through reviews.

Cross-language precision trap

This one bites teams that mix Python services with JS clients.

  • Python time.time() gives float seconds (example: 1719835200.347)
  • JavaScript Date.now() gives integer milliseconds (example: 1719835200347)

If one side truncates and the other side rounds, you can introduce subtle drift in expiry checks or event ordering. It is usually tiny, but in auth and rate-limit logic, tiny can still hurt.

Mixed data in one column

If one service writes seconds and another writes milliseconds into the same BIGINT column, queries look random:

  • sorting becomes weird
  • retention windows are wrong
  • dashboards become untrustworthy

Timezone gets blamed first

Teams often start with timezone debugging. I get it, timezones are scary.

But unit mismatch is usually the first thing to rule out.

A safer way to normalize timestamps

If your schema can carry explicit units, do that. Always.

Field names like these save real debugging hours:

  • created_at_seconds
  • event_time_ms

When you cannot trust input units, normalize early at boundaries.

function toMilliseconds(ts) {
  if (!Number.isFinite(ts)) throw new Error("Invalid timestamp");

  // current-era seconds
  if (ts >= 1e9 && ts < 1e10) return ts * 1000;

  // current-era milliseconds
  if (ts >= 1e12 && ts < 1e13) return ts;

  throw new Error("Unsupported or ambiguous timestamp unit");
}

Python version (same idea):

def to_seconds(ts: int | float) -> float:
    if 1e9 <= ts < 1e10:
        return float(ts)
    if 1e12 <= ts < 1e13:
        return ts / 1000.0
    raise ValueError("Unsupported or ambiguous timestamp unit")

If you need SQL cleanup for mixed historical data, normalize in a view first, then migrate writes to one canonical unit.

My quick debug flow

When timestamps look wrong, I do this in order:

  1. Confirm unit (seconds or milliseconds).
  2. Convert one sample manually and verify expected human time.
  3. Check producer code (Date.now, time.time, DB defaults, queue payloads).
  4. Check consumer assumptions (parsers, ORM models, API contracts).
  5. Only then move to timezone/display debugging.

This order catches most real issues fast.

What to standardize so this does not come back

If you own platform conventions, keep them strict:

  • one unit per field
  • unit in field name
  • range validation on write
  • contract tests between producer and consumer
  • one docs example showing exact unit and sample value

People usually underestimate how much pain this removes.

Quick tools for sanity checks

If you want to verify values quickly:

Final note

This bug is not "junior only." I still see it in mature systems.

Timestamps look like harmless numbers, but once units drift, the damage spreads quietly across auth, analytics, logs, and reporting.

Be explicit about units, normalize at boundaries, and validate ranges.

That is the boring fix that actually works.