Get the number of milliseconds elapsed since the reference time

In JavaScript, a “time value” is a single number: the count of milliseconds since the exact moment 00:00:00 on January 1, 1970, UTC. That moment is the epoch. Negative numbers represent times before the epoch, positive numbers after it. All Date objects are thin wrappers around this number. This is defined by the ECMAScript specification and echoed throughout platform docs.

You can get this millisecond count directly for “now,” compute it for a specific calendar date/time in UTC, or obtain it by parsing a date-time string. Respectively, that’s Date.now(), Date.UTC(...), and Date.parse(...). Each returns a plain number, not a Date instance, and each measures time relative to the same epoch in UTC.

the current timestamp (fast and simple)

Date.now() returns the number of milliseconds that have elapsed since the epoch up to the current instant. It’s the canonical way to grab a timestamp without constructing a Date. It always measures in UTC; there is no “local milliseconds.”

// The current timestamp as a number (e.g., 1737812345678)
const ts = Date.now();

// Use it to measure durations
const t0 = Date.now();
// ... do some work ...
const elapsedMs = Date.now() - t0;
console.log(`Elapsed: ${elapsedMs} ms`);

If you do need a Date object from it, pass the number to the constructor. The object will still wrap the same millisecond value.

const ts = Date.now();
const d  = new Date(ts);
console.log(d.toISOString()); // An ISO 8601 string in UTC

The ISO output is always UTC for toISOString(), which is convenient for logs and debugging.

parse a string into milliseconds

Date.parse takes a date-time string and returns the corresponding milliseconds-since-epoch, or NaN if parsing fails. The tricky part is time zones: a bare date like "2019-01-01" is interpreted as UTC, while a string with both date and time but no offset is interpreted in the local time zone. If the string contains a Z or an explicit offset like +02:00, that offset is used.

// UTC because it's a date-only ISO string
console.log(Date.parse("2019-01-01")); // 1546300800000

// Also UTC because of 'Z'
console.log(Date.parse("2019-01-01T00:00:00.000Z")); // 1546300800000

// Local time (no 'Z' or offset), then converted to UTC milliseconds
// If your machine is UTC+1 in winter, this will be 1 hour earlier in UTC
console.log(Date.parse("2019-01-01T00:00:00"));

When you see unexpected results, it’s usually because the input string didn’t say which time zone it meant. Prefer ISO 8601 with an explicit Z or numeric offset when parsing. The ECMAScript spec defines a simplified ISO 8601 interchange format, which is what engines key off of.

construct a UTC instant from parts

Date.UTC takes individual components and treats them as a UTC calendar time, returning the millisecond count directly. Months are zero-based, just like in the Date constructor. Missing time parts default to zero. This is perfect when you already have numeric fields and you want a stable UTC timestamp without worrying about the local time zone.

// 1970-01-01T00:00:00.000Z
console.log(Date.UTC(1970, 0, 1)); // 0

// 2000-02-29T12:34:56.789Z (leap year handled by the engine)
console.log(Date.UTC(2000, 1, 29, 12, 34, 56, 789)); // 951827696789

// Compare with local-constructor path to highlight the difference:
const localBased = new Date(2000, 1, 29, 12, 34, 56, 789).getTime();
console.log({ utcValue: Date.UTC(2000, 1, 29, 12, 34, 56, 789), localBased });

In the last example, localBased depends on your machine’s time zone, but the Date.UTC result does not. Both are still “milliseconds since the epoch,” but they represent different instants if your locale isn’t UTC.

Getting milliseconds from a Date you already have

Every Date holds a time value internally. Call getTime() (or valueOf()) to get it as a number. This is identical in meaning to what Date.now() returns, just for whatever moment the Date represents.

const d = new Date("2024-06-01T00:00:00Z");
console.log(d.getTime());       // milliseconds since the epoch for that instant
console.log(+d);                // valueOf() — same number via unary +

Time zones, UTC, and why they matter when counting milliseconds

The millisecond count itself is always measured against UTC. It doesn’t “know” about your local clock. What can differ is how you arrive at the instant you want. Parsing a string without an explicit offset may be interpreted in local time first and then converted to UTC milliseconds, whereas Date.UTC treats your components as UTC from the start. Understanding this pipeline explains why two inputs that “look the same” might produce different numbers on different machines.

// Suppose your local zone is Europe/Oslo (UTC+1 standard, UTC+2 in summer).
// Local interpretation then conversion:
const localThenUTC = Date.parse("2024-03-01T00:00:00");
// Explicit UTC:
const explicitUTC  = Date.parse("2024-03-01T00:00:00Z");
console.log({ localThenUTC, explicitUTC, equal: localThenUTC === explicitUTC });

Invalid dates and the representable range (TimeClip)

Not every number is a valid time value. Engines apply the spec’s “TimeClip” rules: if the number is not finite or its magnitude exceeds 8.64e15, the result becomes NaN. That 8.64e15 threshold corresponds to ±100,000,000 days around the epoch, which is about ±285,616 years—ample for real-world apps. Parsing that fails also yields NaN. You can detect invalid dates by checking Number.isNaN(Date.parse(...)) or Number.isNaN(new Date(...).getTime()).

console.log(Date.parse("not a date")); // NaN

// Too large (beyond TimeClip range)
const huge = 1e20;
console.log(new Date(huge).getTime()); // NaN

// Guard against invalid timestamps
const ts = Date.parse("2024-13-01T00:00:00Z"); // invalid month
if (Number.isNaN(ts)) {
  console.error("Invalid timestamp");
}

Practical examples that tie it all together

You can normalize various inputs to a millisecond count for storage or comparison. A typical pattern is to accept either a string, a component bag, or “now,” and return a single number.

/**
 * Normalize different inputs to a UTC millisecond timestamp.
 * - If input is undefined, use the current instant.
 * - If input is a string, parse an ISO 8601 value.
 * - If input is an object with parts, use Date.UTC(parts...).
 */
function toMillis(input) {
  if (input == null) return Date.now();

  if (typeof input === "string") {
    const t = Date.parse(input);
    if (Number.isNaN(t)) {
      throw new Error(`Unparseable date string: ${input}`);
    }
    return t;
  }

  if (typeof input === "object") {
    const {
      year, month, day,
      hour = 0, minute = 0, second = 0, millisecond = 0
    } = input;
    const t = Date.UTC(year, month, day, hour, minute, second, millisecond);
    // Date.UTC always returns a number, but TimeClip can still NaN if components overflow
    if (Number.isNaN(t)) {
      throw new Error(`Out-of-range components: ${JSON.stringify(input)}`);
    }
    return t;
  }

  throw new TypeError("Unsupported input type");
}

// Usage:
console.log(toMillis()); // now
console.log(toMillis("2025-09-28T12:00:00Z")); // explicit UTC
console.log(toMillis({ year: 2025, month: 8, day: 28, hour: 12 })); // 2025-09-28T12:00:00Z

You can round or truncate timestamps for bucketing and indexing. The examples below snap to the start of the day in UTC and to the start of a minute.

const MS_PER_MINUTE = 60 * 1000;
const MS_PER_DAY    = 24 * 60 * MS_PER_MINUTE;

// Start of today in UTC
function startOfUTCDay(ts = Date.now()) {
  const d = new Date(ts);
  const y = d.getUTCFullYear();
  const m = d.getUTCMonth();
  const day = d.getUTCDate();
  return Date.UTC(y, m, day); // safely returns ms at 00:00:00.000Z
}

// Truncate to minute boundary
function floorToMinute(ts = Date.now()) {
  return ts - (ts % MS_PER_MINUTE);
}

console.log(new Date(startOfUTCDay()).toISOString());
console.log(new Date(floorToMinute()).toISOString());

You can compare events irrespective of local time by comparing their millisecond values. The comparison is just numeric; no date math is required.

const a = Date.parse("2025-09-28T10:00:00Z");
const b = Date.parse("2025-09-28T12:00:00+02:00");
console.log(a === b); // true — these represent the same instant

Performance and precision notes

Date.now() is the minimal-overhead timestamp getter for wall-clock time. If you need sub-millisecond timing relative to page load or process start, use the High Resolution Time API (performance.now() in web contexts). performance.now() is not “since 1970” and is purposely not comparable to Date.now(); it’s a monotonic clock suitable for measuring intervals. The W3C spec explicitly distinguishes these notions of time and reiterates that Date measures epoch milliseconds.

// Measuring an operation: choose the clock that fits your need
const t0 = performance.now();
// ... some work ...
const dt = performance.now() - t0; // fractional milliseconds since an arbitrary origin
console.log(`Took ~${dt} ms (high-res)`);

const wallStart = Date.now();
// ... some work ...
const wallElapsed = Date.now() - wallStart; // wall-clock milliseconds since now()
console.log(`Took ~${wallElapsed} ms (wall clock)`);

Date.now() gives you the current epoch milliseconds. Date.parse(s) converts a string into epoch milliseconds, with the nuance that a time-without-offset is interpreted as local time before conversion. Date.UTC(y, m, d, h, M, s, ms) computes epoch milliseconds for a UTC calendar instant from parts. All three speak the same language: “milliseconds since 1970-01-01T00:00:00Z.” The spec defines the range and clipping rules, and engines implement them consistently.

References

SourceWhat it covers
ECMAScript 2026 draft (Numbers and Dates section)Epoch definition, time value semantics, UTC basis.
ECMAScript 2024 (official PDF)Authoritative language snapshot; Date semantics and TimeClip.
MDN: Date.now()Returns milliseconds since the epoch for “now.”
MDN: Date.parse()Parsing behavior, UTC for date-only strings, local for date+time without offset.
MDN: Date.UTC()Constructing UTC instants from components and getting milliseconds directly.
MDN: Date.prototype.getTime()Extracting the wrapped millisecond value from a Date.
MDN: Date overviewGeneral Date behavior and UTC baseline.
W3C High Resolution Time Level 3Distinguishes epoch milliseconds (Date) from monotonic high-res timing.