DEV Community

HAU
HAU

Posted on

Unix Timestamps: The 10 vs 13 Digit Problem That Breaks More APIs Than You Think

There's a bug pattern I've seen in production more times than I can count. It goes like this:

const timestamp = Date.now(); // Returns something like 1742812800000
fetch(`/api/events?since=${timestamp}`);
Enter fullscreen mode Exit fullscreen mode

The backend receives 1742812800000 and interprets it as a Unix timestamp in seconds — which translates to the year 57,212. Cue the confused "why are no events showing up" support ticket.

This is the 10-digit vs 13-digit timestamp problem, and it trips up even experienced developers.

What Is a Unix Timestamp?

A Unix timestamp counts the number of seconds (or milliseconds) elapsed since January 1, 1970, 00:00:00 UTC — also called the Unix epoch.

  • 1742812800 — 10 digits → seconds since epoch
  • 1742812800000 — 13 digits → milliseconds since epoch

The same moment in time, just different units. The problem is that different languages and APIs use different conventions:

Language/Platform Default Unit
JavaScript Date.now() Milliseconds
JavaScript new Date().getTime() Milliseconds
Python time.time() Seconds (float)
Unix/Linux date +%s Seconds
Most REST APIs Seconds
Java System.currentTimeMillis() Milliseconds
Go time.Now().Unix() Seconds
Go time.Now().UnixMilli() Milliseconds

See the mismatch? JavaScript defaults to milliseconds, most backend systems default to seconds.

How to Detect Which One You Have

A quick sanity check:

function detectTimestampUnit(ts) {
  // Current year is ~2026, which is ~1.74 billion seconds
  // or ~1.74 trillion milliseconds from epoch
  const SECONDS_IN_2020 = 1577836800;
  const SECONDS_IN_2100 = 4102444800;

  if (ts >= SECONDS_IN_2020 && ts <= SECONDS_IN_2100) {
    return 'seconds';
  } else if (ts >= SECONDS_IN_2020 * 1000 && ts <= SECONDS_IN_2100 * 1000) {
    return 'milliseconds';
  }
  return 'unknown';
}

console.log(detectTimestampUnit(1742812800));    // "seconds"
console.log(detectTimestampUnit(1742812800000)); // "milliseconds"
Enter fullscreen mode Exit fullscreen mode

Or more bluntly: 10 digits = seconds, 13 digits = milliseconds. This holds true for any timestamp between 2001 and 2286.

The Conversion You Actually Need

// Seconds → Milliseconds (for JavaScript Date)
const tsSeconds = 1742812800;
const date = new Date(tsSeconds * 1000);
console.log(date.toISOString()); // "2026-03-24T08:00:00.000Z"

// Milliseconds → Seconds (for API calls)
const tsMs = Date.now();
const tsForApi = Math.floor(tsMs / 1000);
console.log(tsForApi); // 1742812800

// Get current timestamp in seconds
const now = Math.floor(Date.now() / 1000);
Enter fullscreen mode Exit fullscreen mode

The Year 2038 Problem (Still Real)

If you're working with 32-bit signed integers to store Unix timestamps (common in legacy C/C++ code and older databases), you have a hard ceiling:

  • Max 32-bit signed int: 2,147,483,647
  • That's: January 19, 2038, 03:14:07 UTC

After that, the value overflows to negative — typically interpreted as December 13, 1901. If your system will still be running in 2038 (and many legacy systems will), this needs to be addressed. The fix is migrating to 64-bit integer storage.

Common API Pain Points

GitHub API uses ISO 8601 strings, not timestamps. Don't try to send a number.

Stripe uses seconds-based Unix timestamps throughout.

Twilio uses ISO 8601 as well.

Most database created_at columns store as ISO strings or native datetime types — convert before storing.

Quick Conversion Without Code

When debugging, you often just need a quick sanity check: "does this timestamp look right?"

For that, I keep datetimecalculator.app/unix-timestamp bookmarked. Paste in a 10- or 13-digit number, it auto-detects the unit and shows you the UTC and local time. Works the other way too — pick a date and get the timestamp back. Faster than firing up a Node.js REPL.

Defensive Coding Patterns

// Always be explicit in function signatures
function setExpiry(timestampSeconds) { ... }
function setExpiryMs(timestampMilliseconds) { ... }

// Or use a type wrapper (TypeScript)
type Seconds = number & { readonly __brand: "Seconds" };
type Milliseconds = number & { readonly __brand: "Milliseconds" };

function toSeconds(ms: Milliseconds): Seconds {
  return Math.floor(ms / 1000) as Seconds;
}
Enter fullscreen mode Exit fullscreen mode

Naming your variables and parameters explicitly (timestampSeconds, expiryMs) eliminates an entire class of conversion bugs.

Summary

  • JavaScript gives you milliseconds. Most everything else wants seconds.
  • 10 digits = seconds, 13 digits = milliseconds — use this to sanity-check unknown timestamps.
  • 32-bit timestamp storage has a 2038 deadline. Migrate now if it applies to you.
  • Name your variables explicitly to prevent silent unit mismatches.

Date and time bugs are among the hardest to reproduce in production because they're often time-dependent or timezone-dependent. Getting the fundamentals right upfront saves a lot of pain later.


Hit a timestamp bug in production that cost you hours? I'd love to hear the war story.

Top comments (0)