Skip to content
AldeaCode Logo
Timestamp Converter / JavaScript Developer 100% local

Convert Unix timestamp in JavaScript: Date.now, seconds, ISO 8601

JavaScript dates are easy until you mix milliseconds and seconds, or until you confuse a local Date with a UTC ISO string. Both cost an afternoon if you do not catch them early.

Milliseconds versus seconds

Date.now() returns a Unix timestamp in milliseconds since 1970-01-01 UTC. Most of the JavaScript world works in milliseconds because that is what the platform gives you for free.

The rest of the world works in seconds. Unix time returns seconds. Most APIs (Stripe, Twitter, JWT exp claim, every database unix_timestamp() function) return seconds. If you copy a timestamp from a webhook payload into new Date() directly you get the year 1970, because JavaScript reads it as milliseconds.

The fix is one multiplication. Math.floor(Date.now() / 1000) converts to seconds when you need to send to an external API. new Date(ts * 1000) converts back when you receive seconds. Get this confusion right once and most timestamp bugs disappear.

Date.parse and toISOString

Date.parse(str) returns milliseconds since epoch, not seconds. The name suggests it is symmetric with seconds, but the unit is the same as Date.now(). Always milliseconds.

new Date().toISOString() returns a string in ISO 8601 format with a trailing Z, which means UTC. new Date(2024, 0, 15) creates a Date in local time. If you immediately call .toISOString() on it, the timezone offset gets baked into the output and a user in Madrid sees a different string than a user in Tokyo for the same line of code.

If you want UTC construction explicitly, use Date.UTC(2024, 0, 15) which returns a timestamp number, then wrap with new Date(). The constructor without UTC is local. The serialization is UTC. That mismatch is the most common timestamp bug in JS.

Display with Intl.DateTimeFormat

For display, do not roll your own format string. Intl.DateTimeFormat is built into every modern runtime and handles locale, timezone, and calendar in one call:

new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short",
  timeZone: "America/New_York",
}).format(new Date(ts * 1000));

The locale string controls language. The timeZone option controls which clock you render. Together they cover almost every product need without a library. If you find yourself writing padStart to add a leading zero, you have already gone wrong.

When the stdlib hurts: dayjs and date-fns

Arithmetic on JS Dates is painful. Adding a month is not just +30 days because months are not 30 days. Subtracting two dates and getting a count of business days is not in the standard library.

Two libraries cover most of this without bringing all of Moment with them. dayjs is 2 KB gzipped and reuses a Moment-like API. date-fns is tree-shakeable and exposes pure functions like addMonths(date, 1) and differenceInBusinessDays(a, b).

If your code only needs to format and parse, stay on the stdlib. If you start writing your own addBusinessDays helper, install date-fns and stop maintaining it yourself.

Working example

javascript
// Roundtrip: Date -> Unix seconds -> Date -> ISO 8601

const now = new Date();

// To Unix seconds (matching Stripe, JWT exp, Postgres EXTRACT(epoch))
const unixSeconds = Math.floor(now.getTime() / 1000);
console.log(unixSeconds); // e.g. 1715000000

// Back to Date from seconds (note the * 1000)
const reconstructed = new Date(unixSeconds * 1000);

// ISO 8601 in UTC, the safe interchange format
console.log(reconstructed.toISOString()); // "2026-05-10T12:00:00.000Z"

// Display in a specific locale and timezone
const display = new Intl.DateTimeFormat("en-GB", {
  dateStyle: "medium",
  timeStyle: "short",
  timeZone: "Europe/Madrid",
}).format(reconstructed);
console.log(display);

Just need the result?

When you have a Unix timestamp from a webhook or a log line and you want to see what date it actually represents in your timezone without writing two lines of code, the timestamp converter in aldeacode.com renders the result instantly, supports both seconds and milliseconds, and runs in your browser without sending the value anywhere.

Open Unix Timestamp Converter →

Frequently asked questions

Why does my JWT exp claim show year 56000 in the database?

Almost always a milliseconds vs seconds bug. JWT exp is in seconds (RFC 7519), but Date.now() returns milliseconds. Storing Date.now() directly as exp shifts every timestamp 1000x into the future. Use Math.floor(Date.now() / 1000) when emitting JWTs.

Should I use new Date() or Date.now() for measuring elapsed time?

Use performance.now() for elapsed time within a session, because it is monotonic and not affected by clock changes. Use Date.now() when you need an absolute timestamp to send to a server. Mixing them in the same calculation gives wrong intervals when the user adjusts their clock.

Does toISOString preserve milliseconds?

Yes. The output format is YYYY-MM-DDTHH:mm:ss.sssZ with three decimal places of milliseconds. If you need microseconds, JavaScript Date does not support them at all and you need a different tool, typically a server-side timestamp from the database.