toISOString()
ES5+Returns a string representing the date in the ISO 8601 extended format YYYY-MM-DDTHH:mm:ss.sssZ, always expressed in UTC (the trailing Z). It is the standard serialization format for machine exchange, the same one JSON.stringify uses for Date values via toJSON(), and it throws a RangeError when called on an invalid Date.
Syntax
date.toISOString()Return Value
A string representing the date in ISO format
Examples
const date = new Date('2024-01-15T14:30:00');
console.log(date.toISOString()); 📌 When to Use
Use toISOString() at every machine boundary: request and response bodies, database writes, log lines, cache keys, URLs, and message queues. It is the inverse-friendly companion to Date parsing, since the ISO profile is the only string format ECMAScript guarantees engines parse identically, so a value that round-trips through toISOString() and new Date() is stable across browsers, Node, and workers. Its two structural virtues are worth internalizing. First, it is always UTC with an explicit Z, so a serialized instant means the same moment everywhere, eliminating the ambiguity that plagues bare local strings. Second, its fixed-width, most-significant-first layout makes lexicographic order equal chronological order, so ISO strings sort correctly as plain text in databases, log processors, and Array.prototype.sort without custom comparators. Do not use it as a display format: users should see localized output from toLocaleString or Intl.DateTimeFormat, and the common shortcut of slicing the first ten characters to get "the date" quietly returns the UTC calendar day, which is the previous or next day relative to the user for part of every 24-hour cycle. Validate dates before serializing, because unlike every getter (which return NaN), toISOString() throws on invalid Dates.
⚠️ Common Mistakes
Forgetting the output is UTC. A user in Seoul at 8 AM on March 10 serializes to March 9, 23:00Z; code that then displays the ISO string, or slices its date part, shows yesterday. The Z suffix is a promise about the reference frame, not decoration.
Extracting a local calendar date with toISOString().slice(0, 10). This is the classic birthday-shifts-by-one-day bug: the slice is the UTC date. For a local date string, build it from getFullYear/getMonth/getDate or use toLocaleDateString with a fixed locale like "sv-SE" or "en-CA" that formats as YYYY-MM-DD.
Not guarding invalid dates: new Date(userInput).toISOString() throws RangeError: Invalid time value the first time input is malformed, often deep inside a serializer where the stack trace is unhelpful. Check Number.isNaN(d.getTime()) first.
Assuming the string always has exactly 24 characters. Years outside 0-9999 serialize in expanded form like +010000-01-01T00:00:00.000Z, so fixed-offset slicing of exotic dates (or corrupted timestamps) breaks parsers that assumed a rigid width.
Serializing calendar-only concepts (birthdays, due dates) as full ISO instants. Midnight-UTC-encoded birthdays regress by a day for everyone west of Greenwich the moment any layer localizes them; date-only values should travel as plain YYYY-MM-DD strings.
Expecting timezone information to survive serialization. toISOString() normalizes everything to Z; the original offset is gone. Systems that need to reproduce the user's wall time later must store the IANA zone name alongside the instant.
✅ Best Practices
Adopt "ISO everywhere machines talk, Intl everywhere humans look" as a team convention; most timezone bug classes disappear when serialization and presentation never share a format.
Exploit lexicographic ordering: ISO timestamps as database keys, log prefixes, and filenames (after replacing colons for filesystems) sort chronologically with zero extra logic, which is why S3 keys and log pipelines standardize on them.
Rely on JSON.stringify's automatic Date-to-ISO conversion via toJSON(), but remember JSON.parse does not revive strings into Dates; parse explicitly at the boundary with new Date(value) plus validation, or a reviver function.
Wrap serialization in a helper that validates first: if (Number.isNaN(d.getTime())) throw or return null deliberately, so RangeError never surprises a background job at 3 AM.
When you need "the user's calendar day" rather than an instant, format with Intl.DateTimeFormat using an explicit timeZone rather than slicing ISO strings; and in Temporal-era code, distinguish Temporal.Instant (this format) from PlainDate, which serializes without time or zone by design.
⚡ Performance Notes
toISOString() performs a UTC field breakdown plus fixed-format string assembly; each call allocates a fresh string. It is fast, millions of operations per second in modern engines, but measurably heavier than numeric reads like getTime(), so avoid calling it repeatedly for the same Date inside loops or render passes; serialize once and reuse. In bulk-serialization scenarios (exporting thousands of records), the ISO strings themselves become allocation pressure; when the consumer accepts epoch milliseconds, numbers serialize smaller and faster. Compared with toLocaleString, toISOString() is dramatically cheaper because it involves no locale data or ICU machinery, one reason it is the right default for logs. Sorting by pre-generated ISO strings is as fast as any string sort, but sorting Dates by getTime() remains faster than generating strings just to compare them.
🌍 Real World Example
API Date Serialization
This example shows the serialization discipline production APIs converge on: outgoing payloads carry validated ISO 8601 strings, and incoming strings are parsed and validated in one place rather than ad hoc at every call site. Centralizing both directions creates a single seam where invalid dates are caught (before toISOString can throw) and where policy decisions, rejecting naive strings without offsets, tolerating epoch numbers, live once. The same shape appears in SDK client libraries, form-submission layers, and webhook processors, and it is what makes multi-service systems agree on time.
function createEventPayload(eventData) {
const now = new Date();
return {
...eventData,
createdAt: now.toISOString(),
updatedAt: now.toISOString(),
scheduledFor: new Date(eventData.scheduledFor).toISOString(),
metadata: {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
localTime: now.toLocaleString()
}
};
}
const payload = createEventPayload({
title: 'Team Meeting',
scheduledFor: '2024-03-20T14:00:00'
});
console.log(JSON.stringify(payload, null, 2));
// {
// "title": "Team Meeting",
// "createdAt": "2024-03-15T08:30:00.000Z",
// "updatedAt": "2024-03-15T08:30:00.000Z",
// "scheduledFor": "2024-03-20T14:00:00.000Z",
// ...
// }