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
| Bug | Cause | Fix |
|---|---|---|
| Date in 1970 | Passed seconds to new Date() without × 1000 | Multiply seconds by 1000 |
Invalid Date | Passed a string instead of a number | Coerce with Number(ts) |
| Wrong timezone | Used toLocaleDateString() without specifying timezone | Use toISOString() or Intl.DateTimeFormat with explicit timeZone |
| Lost milliseconds | Called parseInt() on a float timestamp | Use ts * 1000 directly, not parseInt(ts) * 1000 |
| Year 58,000 | Double-multiplied Date.now() | Date.now() is already milliseconds |
Further Reading
- MDN: Date
- MDN: Intl.DateTimeFormat
- biztechbridge-tools/epoch-converter — the full React source for our converter
Try it in your browser
No setup needed — use our free Epoch Converter directly online.