Debugging Unix Timestamps in JavaScript: A Practical Guide

Published javascriptunix-timestampdebuggingwebdev

Every JavaScript developer eventually hits this moment: an API returns a timestamp, you pass it to new Date(), and you get back either a wildly wrong date or Invalid Date. This guide covers every common Unix timestamp bug in JS — and exactly how to fix each one.


The Root Cause: Seconds vs Milliseconds

JavaScript’s Date always works in milliseconds. The Unix standard uses seconds. Mix them up and you get dates in 1970 or the year 56,000.

// ❌ Wrong — treating seconds as milliseconds
new Date(1712700000);
// → Thu Jan 01 1970 00:28:32 GMT+0000
// (1.7 billion milliseconds = ~20 days after epoch, not April 2024)

// ✅ Correct — convert seconds to milliseconds first
new Date(1712700000 * 1000);
// → Tue Apr 09 2024 20:00:00 GMT+0000

Rule of thumb: 10-digit timestamp = seconds. 13-digit = milliseconds.


Bug #1: Invalid Date

const ts = "1712700000";
new Date(ts); // → "Invalid Date" in some environments

new Date() accepts a number, not a numeric string. Always coerce first:

const ts = "1712700000";
new Date(Number(ts) * 1000); // ✅
// or
new Date(+ts * 1000);        // ✅ (unary + coerces to number)

Bug #2: Off-by-one-hour (Timezone Traps)

const date = new Date(1712700000 * 1000);
console.log(date.toLocaleDateString()); // varies by machine timezone!

toLocaleDateString(), toLocaleString(), and toString() all use the machine’s local timezone. The same code returns different results on a server in UTC vs a laptop in New York.

Fix: always be explicit about the timezone:

const date = new Date(1712700000 * 1000);

// Always UTC
console.log(date.toISOString());
// → "2024-04-09T20:00:00.000Z" ✅ same everywhere

// Explicit timezone via Intl
console.log(new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  dateStyle: 'full',
  timeStyle: 'short',
}).format(date));
// → "Tuesday, April 9, 2024 at 4:00 PM" ✅

Bug #3: Floating-Point Timestamps from APIs

Some APIs return timestamps as floats (e.g. 1712700000.456). Multiplying by 1000 gives 1712700000456 — which is fine — but if you parseInt before multiplying, you lose the sub-second precision silently.

const ts = 1712700000.456;

// ❌ loses milliseconds
new Date(parseInt(ts) * 1000);   // → 2024-04-09T20:00:00.000Z

// ✅ preserves milliseconds
new Date(ts * 1000);              // → 2024-04-09T20:00:00.456Z
new Date(Math.round(ts * 1000)); // → 2024-04-09T20:00:00.456Z

Bug #4: Date.now() Returns Milliseconds — Don’t Double-Convert

// ❌ wrong — Date.now() is already milliseconds
const epoch = Date.now();
new Date(epoch * 1000); // → year 58,000+

// ✅ correct
new Date(Date.now()); // milliseconds directly
Math.floor(Date.now() / 1000); // → seconds, for APIs that expect seconds

A Debug Helper Worth Adding to Every Project

Paste this into your dev utilities. It auto-detects seconds vs milliseconds, accepts strings and numbers, and always returns an ISO string:

/**
 * Safely parse a Unix timestamp to an ISO date string.
 * Handles both seconds (10-digit) and milliseconds (13-digit).
 * Returns null for invalid input instead of throwing.
 */
function parseTimestamp(ts) {
  const n = Number(ts);
  if (!Number.isFinite(n) || n <= 0) return null;

  // Heuristic: if less than 1e11, assume seconds; otherwise milliseconds
  const ms = n < 1e11 ? n * 1000 : n;
  const date = new Date(ms);

  return isNaN(date.getTime()) ? null : date.toISOString();
}

// Usage
parseTimestamp(1712700000);      // → "2024-04-09T20:00:00.000Z"
parseTimestamp("1712700000");    // → "2024-04-09T20:00:00.000Z"
parseTimestamp(1712700000000);   // → "2024-04-09T20:00:00.000Z"
parseTimestamp("not a date");    // → null
parseTimestamp(-1);              // → null

Quick Sanity-Check Without Writing Code

When you just need to verify a timestamp during debugging or a code review, paste it directly into our online timestamp converter. It handles both seconds and milliseconds automatically, shows UTC and local time side-by-side, and runs entirely in your browser — nothing is sent to a server.


Summary

BugCauseFix
Date in 1970Passed seconds to new Date() without × 1000Multiply seconds by 1000
Invalid DatePassed a string instead of a numberCoerce with Number(ts)
Wrong timezoneUsed toLocaleDateString() without specifying timezoneUse toISOString() or Intl.DateTimeFormat with explicit timeZone
Lost millisecondsCalled parseInt() on a float timestampUse ts * 1000 directly, not parseInt(ts) * 1000
Year 58,000Double-multiplied Date.now()Date.now() is already milliseconds

Further Reading

Try it in your browser

No setup needed — use our free Epoch Converter directly online.

Open Epoch Converter →

Sven Schuchardt

Management Consulting · Enterprise Architecture

Bridging the gap between business need and IT & Architecture enablers. With a background in management consulting and enterprise architecture, translating complex technology decisions into clear, actionable insights — written for every stakeholder, from the boardroom to the engineering team.

Connect on LinkedIn