Unix Time in JavaScript

JavaScript counts epoch time in milliseconds, not seconds. Date.now() returns the current count — a thirteen-digit number for any modern date — and every Date object is a thin wrapper around one such number. Nearly every JavaScript timestamp bug is a missing or extra factor of 1000 at the boundary with systems that count in seconds.

For Unix-convention seconds, divide and truncate: Math.floor(Date.now() / 1000).

The essential conversions

  • Current milliseconds: Date.now(). Current seconds: Math.floor(Date.now() / 1000).
  • Seconds to Date: new Date(seconds * 1000). Milliseconds to Date: new Date(ms).
  • Date to milliseconds: date.getTime() (or +date). Date to seconds: Math.floor(date.getTime() / 1000).
  • ISO string to timestamp: Date.parse('2038-01-19T03:14:07Z') returns milliseconds; new Date(string).getTime() is equivalent.

Ten digits or thirteen: guarding the boundary

APIs, JWTs and databases usually speak seconds; JavaScript speaks milliseconds. Feed a ten-digit seconds value straight into new Date() and every date lands in January 1970; multiply an already-millisecond value by 1000 and dates land tens of thousands of years out. When accepting timestamps from outside, check the magnitude — a value below about 100 billion is almost certainly seconds — and normalise once, at the edge, instead of sprinkling *1000 through the codebase.

Display: locales and time zones without libraries

date.toISOString() always prints UTC, which is what logs and APIs want. For human display, Intl.DateTimeFormat handles locale and timezone natively: new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium', timeStyle: 'short', timeZone: 'Europe/Istanbul' }).format(date). This replaces most historical uses of moment.js.

Note that the legacy Date parser is reliable only for full ISO 8601 strings; formats like '03/04/2026' parse differently across locales and engines and are best avoided entirely.

Temporal: the modern API

The Temporal proposal replaces Date with precise, immutable types: Temporal.Now.instant() for the current moment, Temporal.Instant.fromEpochMilliseconds(ms) for conversions, and epochMilliseconds to read the count back. It also distinguishes instants from zoned and plain dates, removing the ambiguity Date was built on. It is shipping in Firefox and is behind flags elsewhere; for portable code today, Date plus Intl remains the baseline.

Frequently asked questions

How do I get the current Unix timestamp in JavaScript?

Date.now() returns milliseconds since the epoch. For Unix-convention seconds use Math.floor(Date.now() / 1000).

Is JavaScript epoch time in seconds or milliseconds?

Milliseconds — thirteen digits for modern dates. Unix-convention seconds are ten digits, so convert with *1000 or /1000 at the boundary.

How do I convert a Unix timestamp to a date in JavaScript?

new Date(seconds * 1000) for a seconds timestamp, or new Date(ms) if the value is already in milliseconds. A date landing in 1970 means the *1000 is missing.

How do I format a timestamp for a specific time zone in JavaScript?

Use Intl.DateTimeFormat with the timeZone option, e.g. new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium', timeStyle: 'short', timeZone: 'America/New_York' }).format(new Date(ts * 1000)). No library needed.

What is the Temporal API?

The successor to Date: immutable, nanosecond-precision types that separate instants from zoned and plain dates. Temporal.Now.instant() and Temporal.Instant.fromEpochMilliseconds() cover the epoch conversions. It is not yet available in all browsers.