The short answer
Date.now() returns the current Unix timestamp in milliseconds.
const nowMs = Date.now(); // 13 digits, epoch milliseconds
const nowSeconds = Math.floor(nowMs / 1000); // 10 digits, Unix seconds
const date = new Date(nowMs); // Date expects milliseconds
Use the value like this:
| Task | Copy this | Unit |
|---|---|---|
| Current timestamp for JavaScript Date | Date.now() |
milliseconds |
| Current Unix timestamp for most APIs | Math.floor(Date.now() / 1000) |
seconds |
| Convert Unix seconds to Date | new Date(seconds * 1000) |
input seconds |
| Convert epoch milliseconds to Date | new Date(milliseconds) |
input milliseconds |
| Format as ISO UTC | new Date(ms).toISOString() |
output string |
| Measure duration | performance.now() |
monotonic milliseconds |
The fastest sanity check is the digit count. A modern Unix seconds value is usually 10 digits. A JavaScript timestamp from Date.now() is usually 13 digits.
What Date.now() actually returns
Date.now() returns a number: the elapsed milliseconds since 1970-01-01 00:00:00 UTC. It does not return a Date object, does not store a timezone, and does not tell you how long a function took to run.
MDN describes Date objects as encapsulating an integral millisecond value since the Unix epoch. That value is timezone-agnostic; formatting methods decide later whether to show UTC, the local timezone, or a named IANA timezone.
const ms = Date.now();
console.log(ms); // 1781947500000 style value
console.log(new Date(ms)); // Date object for that instant
console.log(new Date(ms).toISOString()); // UTC string
For the fixed example used throughout this guide:
| Value | Interpretation | Result |
|---|---|---|
1781947500000 |
JavaScript / Unix milliseconds | 2026-06-20T09:25:00.000Z |
1781947500 |
Unix seconds | 2026-06-20T09:25:00.000Z |
1781947500 passed directly to new Date() |
milliseconds by mistake | 1970-01-21T14:59:07.500Z |
Get seconds or milliseconds without guessing
The right value depends on who receives it.
// Browser UI, JS Date, localStorage, client events
const createdAtMs = Date.now();
// Server APIs, JWT exp/iat, Unix-style database filters
const createdAtSeconds = Math.floor(Date.now() / 1000);
Use milliseconds when the next line of code is JavaScript:
new Date(createdAtMs)- browser analytics events
- frontend cache expiry
- React/Vue/Svelte UI state
- APIs that explicitly document epoch milliseconds
Use seconds when the value crosses into Unix-style systems:
- JWT
exp,iat, andnbf - Stripe-style API fields that document Unix seconds
- SQL epoch filters that expect seconds
- Linux shell commands such as
date -d @SECONDS - PHP
time()and Pythonint(time.time())style payloads
Name the field with the unit. createdAt is a shrug. createdAtMs, createdAtUnixSeconds, and expiresAtEpochMs are boring in the best possible way.
The classic 1970 bug
This is the bug every JavaScript timestamp article has to answer.
new Date(1700000000).toISOString();
// "1970-01-20T16:13:20.000Z" <-- wrong for Unix seconds
new Date(1700000000 * 1000).toISOString();
// "2023-11-14T22:13:20.000Z" <-- correct
new Date(number) expects milliseconds. If your input is a 10-digit Unix seconds value, multiply by 1000 first.
A small helper is fine at the boundary, but do not let auto-detection hide a sloppy API contract forever:
function unixToDate(value) {
const n = Number(value);
if (!Number.isFinite(n)) throw new TypeError("timestamp must be numeric");
return new Date(Math.abs(n) < 1e11 ? n * 1000 : n);
}
That 1e11 threshold works for normal contemporary timestamps because Unix seconds stay below it for thousands of years, while current epoch milliseconds are already far above it. For archival, scientific, or intentionally ancient/future dates, require the caller to specify the unit instead.
Date.now() vs new Date().getTime()
These two values are equivalent:
Date.now();
new Date().getTime();
The difference is that Date.now() is a static method and does not allocate a Date object. In one-off code, nobody will notice. In a tight loop, request logger, analytics collector, or render-frame callback, Date.now() is the cleaner default.
Use new Date() when you need a Date object immediately:
const date = new Date();
const iso = date.toISOString();
Use Date.now() when you only need the numeric timestamp:
const sentAtMs = Date.now();
Date.now() vs performance.now()
Do not use Date.now() for elapsed-time measurement.
Date.now() reads the wall clock. The wall clock can jump when the user changes the system time, a VM resumes, or a time synchronization service adjusts the clock. performance.now() is relative to performance.timeOrigin, gives sub-millisecond values, and is monotonic.
// Good for "when did this event happen?"
const receivedAtMs = Date.now();
// Good for "how long did this operation take?"
const start = performance.now();
await doWork();
const elapsedMs = performance.now() - start;
Use this rule:
| Question | API |
|---|---|
| When did it happen? | Date.now() |
| How long did it take? | performance.now() |
| What should I put in an API payload? | documented seconds or milliseconds |
| What should I show a user? | Intl.DateTimeFormat |
Format a timestamp in UTC or a user timezone
A Date stores the instant. Formatting chooses the timezone.
const date = new Date(1781947500000);
date.toISOString();
// "2026-06-20T09:25:00.000Z"
new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
timeZone: "America/New_York",
}).format(date);
// "Jun 20, 2026, 5:25 AM"
Always pass timeZone in server-rendered output, scheduled emails, tests, screenshots, and documentation examples. Without it, the runtime uses its local timezone, so your laptop and production server can format the same timestamp differently.
For repeated formatting, cache the formatter:
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
dateStyle: "medium",
timeStyle: "medium",
});
rows.map((row) => fmt.format(new Date(row.createdAtMs)));
MDN documents timeZone as accepting IANA names such as UTC and America/New_York; the default is the runtime timezone.
Parse date strings safely
JavaScript reliably supports the ECMAScript date-time string format, which is ISO-8601-like. Other input formats are implementation-defined.
Use these:
new Date("2026-06-20T09:25:00Z"); // UTC
new Date("2026-06-20T09:25:00-04:00"); // explicit offset
Date.parse("1970-01-01T00:00:00Z"); // 0
Avoid these at system boundaries:
new Date("06/20/2026"); // non-standard format
new Date("01/02/2026"); // ambiguous for humans and brittle in code review
new Date("Jun 20, 2026"); // legacy parser territory
One subtle rule is worth remembering: a date-only string such as 2019-01-01 is interpreted as UTC by the standard date-time string rules, while a date-time string without a timezone such as 2019-01-01T00:00:00 is interpreted in the local timezone. That difference surprises people during tests.
For user-entered dates, parse with a format-aware library or Temporal, not a hopeful new Date(input).
Validate timestamp input before converting
If a timestamp comes from an API request, CSV import, webhook, browser event, or query string, validate it before constructing a Date.
function parseEpochMs(input) {
if (typeof input !== "number" && typeof input !== "string") {
throw new TypeError("timestamp must be a number or numeric string");
}
const n = Number(input);
if (!Number.isFinite(n)) throw new TypeError("timestamp is not finite");
const ms = Math.abs(n) < 1e11 ? n * 1000 : n;
const date = new Date(ms);
if (Number.isNaN(date.getTime())) throw new RangeError("timestamp is outside Date range");
return { ms, date };
}
Useful guardrails:
- Reject empty strings before
Number("")turns them into0. - Reject boolean values before
Number(true)turns them into1. - Keep the original raw value in logs when rejecting a webhook or CSV row.
- Store validated timestamps in one unit internally.
- Convert at the boundary, not in every call site.
Sub-millisecond precision in JavaScript
Date.now() gives integer milliseconds. For sub-millisecond elapsed timing, use performance.now().
const absoluteMs = performance.timeOrigin + performance.now();
That expression produces an absolute timestamp-like value in milliseconds, but treat it carefully. Browser timers may be rounded for privacy and security reasons, and each worker or browsing context has its own time origin. For Node.js, node:perf_hooks exposes performance.now() and performance.timeOrigin; process.hrtime.bigint() gives monotonic nanoseconds for elapsed timing.
Use sub-millisecond values for measurement. Do not send them to an API that only documents Unix seconds or Unix milliseconds.
Temporal: better dates, but check availability
Temporal is the modern JavaScript date-time API. It separates concepts that Date mixes together: instants, plain dates, wall-clock times, zoned date-times, durations, and calendars.
Use Temporal for:
- adding days or months across daylight saving time
- representing a calendar date without a timezone
- converting a wall-clock appointment plus IANA timezone into an instant
- avoiding mutable Date methods
- codebases that can require Node.js 26+ or ship a polyfill
Example:
const instant = Temporal.Instant.fromEpochMilliseconds(Date.now());
const zdt = instant.toZonedDateTimeISO("America/New_York");
const tomorrowSameWallClock = zdt.add({ days: 1 });
As of July 15, 2026, MDN still marks Temporal as limited availability because it does not work in some widely used browsers. Node.js 26 enables Temporal by default. For public web apps, keep the polyfill or feature-detect before relying on native Temporal everywhere.
Common mistakes and fixes
| Symptom | Cause | Fix |
|---|---|---|
| Date shows January 1970 | Unix seconds passed to new Date() |
multiply by 1000 |
| Date shows a far-future year | milliseconds sent to a seconds API | divide by 1000 and floor |
| Test passes locally but fails in CI | runtime timezone differs | pass timeZone, or test ISO UTC |
| Elapsed time is negative | wall clock moved | use performance.now() |
| Equality check fails | comparing Date objects | compare .getTime() values |
| User date shifts one day | date-only vs timezone mismatch | model date-only values separately |
| DST adds/subtracts an hour | raw millisecond calendar math | use Temporal or a timezone-aware library |
The one-line code-review question is: "What unit and timezone does this value have at this boundary?"
Copy-paste recipes
// Current epoch milliseconds
const nowMs = Date.now();
// Current Unix seconds
const nowSeconds = Math.floor(Date.now() / 1000);
// Unix seconds -> ISO string
const iso = new Date(1700000000 * 1000).toISOString();
// Epoch milliseconds -> ISO string
const isoFromMs = new Date(1700000000000).toISOString();
// Date -> epoch milliseconds
const ms = new Date("2026-06-20T09:25:00Z").getTime();
// Date -> Unix seconds
const seconds = Math.floor(new Date("2026-06-20T09:25:00Z").getTime() / 1000);
// Format in UTC
const utcText = new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
dateStyle: "medium",
timeStyle: "medium",
}).format(new Date());
// Format in a named timezone
const nyText = new Intl.DateTimeFormat("en-US", {
timeZone: "America/New_York",
dateStyle: "medium",
timeStyle: "medium",
}).format(new Date());
Related JavaScript timestamp guides
This page is the Date.now entry point for the JavaScript timestamp cluster. Use the companion pages when the task gets more specific:
FAQ
- Does Date.now() return seconds or milliseconds?
- Milliseconds. Date.now() returns the number of milliseconds since 1970-01-01 00:00:00 UTC. In 2026 that is usually a 13-digit value. For Unix seconds, use Math.floor(Date.now() / 1000).
- How do I get the current Unix timestamp in JavaScript?
- Use Date.now() for Unix milliseconds. Use Math.floor(Date.now() / 1000) for Unix seconds, which is the form many server APIs, JWT claims, and Unix tools expect.
- Why does new Date(1700000000) show a date in 1970?
- Because new Date(number) expects milliseconds. 1700000000 is a 10-digit Unix seconds value, so JavaScript reads it as 1,700,000,000 milliseconds after the epoch. Use new Date(1700000000 * 1000).
- Does a JavaScript Date store a timezone?
- No. A Date stores one timestamp value. Local timezone, UTC, and named timezones are display choices applied later by methods such as toString(), toISOString(), toLocaleString(), and Intl.DateTimeFormat.
- When should I use performance.now() instead of Date.now()?
- Use performance.now() for elapsed time, benchmarks, animation loops, and timeouts. Date.now() reads wall-clock time and can move if the system clock changes; performance.now() is monotonic and is not subject to wall-clock adjustments.
- How do I convert Unix seconds to a JavaScript Date?
- Multiply seconds by 1000 before constructing the Date: new Date(seconds * 1000). If the input is already epoch milliseconds, pass it directly: new Date(milliseconds).
- How do I format a JavaScript Date in a specific timezone?
- Use Intl.DateTimeFormat or toLocaleString with a timeZone option, such as America/New_York or UTC. Without the option, JavaScript uses the runtime's local timezone.
- Is Date.parse() safe for all date strings?
- No. The ISO date-time string format is portable, but other formats are implementation-defined. Prefer strings like 2026-06-20T09:25:00Z or include an explicit offset.
- Should I use Temporal instead of Date?
- Use Temporal where it is available or where you can ship a polyfill, especially for timezone and calendar arithmetic. MDN still marks Temporal as limited availability, while Node.js 26 enables it by default. Date remains fine for simple timestamp reads and broad browser support.
- Is Date.now() faster than new Date().getTime()?
- Usually yes, because Date.now() does not allocate a Date object. The difference rarely matters for occasional reads, but Date.now() is the cleaner default in hot paths.