DEV Community

Cover image for How to Convert a Unix Timestamp to a Date in JavaScript
Muhammad Wasif
Muhammad Wasif

Posted on Originally published at cybercodelab.online

How to Convert a Unix Timestamp to a Date in JavaScript

To convert a Unix timestamp to a date in JavaScript, multiply it by 1000 and pass it to the Date constructor: new Date(timestamp * 1000). The multiplication is the whole trick — Unix timestamps count seconds since 1 January 1970, but JavaScript's Date counts milliseconds. Skip the * 1000 and your 2026 date silently becomes January 1970.

const timestamp = 1786000000;
const date = new Date(timestamp * 1000);

console.log(date.toISOString());
// "2026-08-06T07:06:40.000Z"
Enter fullscreen mode Exit fullscreen mode

That's the answer. The rest of this post covers the parts that actually bite people: telling seconds from milliseconds, formatting the result for humans, and converting back the other way.

Why does my timestamp show 1970?

Because the value is in seconds and new Date() expects milliseconds. This is the single most common bug in timestamp code, and it fails quietly:

new Date(1786000000).toISOString();
// "1970-01-21T16:06:40.000Z"  ← wrong, forgot the * 1000
Enter fullscreen mode Exit fullscreen mode

Twenty-one days after the epoch instead of the year 2026. No error, no warning — just a wrong date sitting in your database.

The reason is that the two worlds disagree on units. Unix time — used by C, Python, PHP, MySQL, most REST APIs and every server log file — counts seconds. The ECMAScript specification defines a JavaScript Date as a number of milliseconds since the same 1970 epoch, which is why Date.now() returns a 13-digit number. Anything crossing that boundary needs a factor of 1000.

Seconds or milliseconds? Count the digits

For any date in the current era the answer is unambiguous:

Digits Unit Example Means
10 Seconds 1786000000 6 Aug 2026
13 Milliseconds 1786000000000 6 Aug 2026
16 Microseconds 1786000000000000 6 Aug 2026

Ten digits means seconds and needs the * 1000. Thirteen digits is already milliseconds — pass it straight to new Date(). If you're handling both (say, from two different APIs), normalise defensively:

function toDate(value) {
  const n = Number(value);
  // 13+ digits is already milliseconds
  return new Date(n < 1e11 ? n * 1000 : n);
}
Enter fullscreen mode Exit fullscreen mode

The 1e11 threshold works because 100,000,000,000 seconds lands in the year 5138 — far beyond anything a real seconds-based timestamp will contain, and far below any millisecond timestamp from the last 50 years.

Formatting it for humans

new Date() returns a Date object, not text. The default output is machine-shaped:

const date = new Date(1786000000 * 1000);

date.toISOString();
// "2026-08-06T07:06:40.000Z"   ← always UTC, good for storage and APIs

date.toString();
// A long local-time string that varies by machine — avoid for output
Enter fullscreen mode Exit fullscreen mode

For anything a person reads, use toLocaleString() with an explicit locale and time zone. Being explicit is what makes the output predictable instead of "whatever the server happens to be set to":

const date = new Date(1786000000 * 1000);

date.toLocaleString('en-GB', {
  timeZone: 'UTC',
  dateStyle: 'full',
  timeStyle: 'short',
});
// "Thursday, 6 August 2026 at 07:06"

date.toLocaleString('en-GB', {
  timeZone: 'Asia/Karachi',
  dateStyle: 'full',
  timeStyle: 'short',
});
// "Thursday, 6 August 2026 at 12:06"

date.toLocaleString('en-US', {
  timeZone: 'America/New_York',
  dateStyle: 'medium',
  timeStyle: 'short',
});
// "Aug 6, 2026, 3:06 AM"
Enter fullscreen mode Exit fullscreen mode

One timestamp, three different wall-clock readings — because a timestamp is a single instant, and the time zone is applied only at display time. That's a feature, not a bug: store the number, format at the edge.

Going the other way

To produce a timestamp instead of consuming one, divide by 1000 and floor it:

// Current time as a Unix timestamp (seconds)
const now = Math.floor(Date.now() / 1000);

// A specific date as a Unix timestamp (seconds)
const ts = Math.floor(new Date('2026-08-06T07:06:40Z').getTime() / 1000);
// 1786000000
Enter fullscreen mode Exit fullscreen mode

Two details worth getting right:

  • Use Math.floor(), not Math.round(). Rounding can push you a full second into the future, which breaks "not valid before" comparisons and JWT expiry checks.
  • Include the Z (or a +05:00 style offset) when parsing a date string. new Date('2026-08-06T07:06:40Z') is unambiguously UTC; new Date('2026-08-06 07:06:40') is interpreted in the browser's local zone, so identical code produces different timestamps on different machines.

Common mistakes, and the fixes

Mistake What happens Fix
new Date(seconds) Date lands in Jan 1970 new Date(seconds * 1000)
Date.now() sent to a seconds-based API Timestamp ~1000× too large Math.floor(Date.now() / 1000)
Parsing "2026-08-06 07:06:40" Result depends on the machine's zone Use ISO format with Z
Math.round() when converting to seconds Occasionally one second ahead Math.floor()
Formatting with toString() Output differs per server and browser toLocaleString() with explicit timeZone
Storing formatted text in the database Zone information is lost forever Store the timestamp or a UTC ISO string

That last row is the one that costs the most later. Store the instant; format on the way out.

A live clock, same conversion

setInterval(() => {
  const seconds = Math.floor(Date.now() / 1000);
  const label = new Date(seconds * 1000).toLocaleTimeString('en-GB', {
    timeZone: 'UTC',
    hour12: false,
  });
  document.getElementById('clock').textContent = label;
}, 1000);
Enter fullscreen mode Exit fullscreen mode

A few things people ask

Does new Date(timestamp * 1000) apply my local time zone?
No. The Date object stores a single UTC instant; the time zone appears only when you format it. toISOString() always prints UTC, while toLocaleString() uses the browser's zone unless you pass an explicit timeZone option.

Can JavaScript handle timestamps from before 1970?
Yes. Negative timestamps work normally: new Date(-86400 * 1000) returns 31 December 1969. JavaScript's Date spans roughly ±8.64 × 10¹⁵ milliseconds around 1970 — about 273,790 years in each direction. Anything outside that returns Invalid Date.

Is JavaScript affected by the Year 2038 problem?
No. The 2038 problem hits systems storing timestamps in a signed 32-bit integer, which overflows at 2147483647 seconds — 19 January 2038, 03:14:07 UTC. JavaScript stores time as a 64-bit float of milliseconds, so it sails past 2038. You can still receive a broken value from a 32-bit backend, so the fix belongs on that side.


The rule is short enough to memorise: seconds in Unix, milliseconds in JavaScript, * 1000 in between. Get that one factor right and the rest of the Date API stops being mysterious.

If you want to sanity-check a raw number from a log file or a JWT payload without opening a console, I keep a free Unix Timestamp Converter that shows the real date instantly.

Originally published at cybercodelab.online.

Top comments (0)