Standard Library: Dates, Errors & JSON

This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page covers three related pillars of the standard library: representing and formatting dates/times with Date, signaling and handling failures with Error and try/catch/finally, and serializing data with JSON.

The Date Object

Date is JavaScript’s API for working with dates and times. Internally, a Date stores a single integer: the number of milliseconds since (or before) midnight on January 1, 1970, UTC — the same "Unix epoch" convention used across most programming languages.

Construction

let now = new Date();                 // The current date and time
let epoch = new Date(0);              // Midnight, January 1st, 1970, UTC
let century = new Date(2100,          // Year 2100
                        0,            // January (months are 0-indexed!)
                        1,            // 1st (days-of-month are 1-indexed)
                        2, 3, 4, 5);  // 02:03:04.005, in the *local* time zone

// Build a date in UTC instead of local time:
let centuryUtc = new Date(Date.UTC(2100, 0, 1));

// Parse an ISO-8601 string:
let parsed = new Date("2100-01-01T00:00:00Z");
The first month of a year is 0 (January), but the first day of a month is 1 — a well-known source of off-by-one bugs. Any field omitted from a multi-argument constructor call defaults to 0.

Getters, Setters & Timestamps

Every field of a Date has a local-time getter/setter pair and a UTC-time getter/setter pair, following the pattern get<Field>() / getUTC<Field>() and set<Field>() / setUTC<Field>(), for FullYear, Month, Date (day-of-month), Hours, Minutes, Seconds, and Milliseconds:

let d = new Date();
d.setFullYear(d.getFullYear() + 1);   // Increment the year
d.getDay();                            // Day of week: 0 (Sunday) .. 6 (Saturday), read-only

getTime()/setTime() read and write the raw millisecond timestamp directly, which is convenient for simple arithmetic:

d.setTime(d.getTime() + 30_000);   // Add 30 seconds

let start = Date.now();            // Static method: current time as a timestamp (no Date object needed)
doSomethingSlow();
console.log(`Took ${Date.now() - start}ms`);
For sub-millisecond timing precision (e.g. micro-benchmarks), the browser/Node performance.now() function returns a non-integer millisecond value measured relative to page load or process start, not an absolute timestamp like Date.now(). Browsers may deliberately reduce its precision for fingerprinting resistance.

Date Arithmetic

Date objects support <, , >, >= directly, and subtracting one Date from another yields the millisecond difference between them (because Date defines a valueOf() that returns its timestamp). Adding or subtracting seconds/minutes/hours is easiest via the raw timestamp, as above; adding days, months, or years — which have varying lengths — should go through setDate()/setMonth()/setFullYear(), which correctly roll over into the next month/year on overflow:

let d = new Date();
d.setMonth(d.getMonth() + 3, d.getDate() + 14);  // +3 months, +14 days, rolling over years as needed

Formatting & Parsing

Method Behavior

toString()

Local time zone, not locale-aware.

toUTCString()

UTC time zone, not locale-aware.

toISOString()

ISO-8601 (YYYY-MM-DDTHH:mm:ss.sssZ), always UTC — the format to use for interchange/storage.

toLocaleString()

Local time zone, locale-appropriate format (date + time).

toDateString() / toLocaleDateString()

Date portion only; the Locale variant is locale-aware.

toTimeString() / toLocaleTimeString()

Time portion only; the Locale variant is locale-aware.

None of these are ideal for displaying dates to end users in a fully locale-correct way — see Standard Library: Internationalization for Intl.DateTimeFormat, which is the general-purpose, locale-aware tool for that job.

Date.parse(string) is the static counterpart to the string constructor: it parses a string into a millisecond timestamp, and is guaranteed to understand the output of toISOString(), toUTCString(), and toString().

Error Classes

throw and catch can operate on any JavaScript value, not just Error instances — but using Error (or a subclass) is strongly conventional, because constructing an Error captures the current call stack, which is invaluable for debugging an uncaught (or logged) exception.

try {
  riskyOperation();
} catch (err) {
  console.error(err.message);   // The string passed to the Error() constructor
  console.error(err.name);      // "Error" for the base class
  console.error(err.stack);     // Non-standard but universally supported: a multi-line stack trace
} finally {
  cleanup();   // Always runs, whether or not an exception was thrown/caught
}
err.stack captures where the Error object was created (new Error(…​)), not where it was throw`n. Creating and throwing in the same statement (`throw new Error(…​)) keeps the two in sync.

JavaScript also predefines several Error subclasses for specific ECMAScript-level failure conditions: EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError. Each has a name matching its constructor and takes the same single message argument as Error.

Custom Error Subclasses

Application code commonly defines its own Error subclasses to carry structured detail beyond name/message, using the same extends/super() mechanics covered in Classes:

class HTTPError extends Error {
  constructor(status, statusText, url) {
    super(`${status} ${statusText}: ${url}`);
    this.status = status;
    this.statusText = statusText;
    this.url = url;
  }

  get name() { return "HTTPError"; }
}

let error = new HTTPError(404, "Not Found", "https://example.com/");
error.status;   // => 404
error.message;  // => "404 Not Found: https://example.com/"

For the full if/switch/loop toolbox that error-handling code is typically embedded in, see Statements.

JSON.stringify() / JSON.parse()

JSON ("JavaScript Object Notation") is JavaScript’s built-in serialization format for converting in-memory data structures to and from strings, using JavaScript’s own object/array literal syntax. It supports numbers, strings, booleans, null, arrays, and plain objects — but not Map, Set, RegExp, Date, or typed arrays natively.

let o = { s: "", n: 0, a: [true, false, null] };
let s = JSON.stringify(o);   // '{"s":"","n":0,"a":[true,false,null]}'
let copy = JSON.parse(s);    // A structurally-equal deep copy of o

// A quick (if inefficient) deep-clone trick for anything JSON-serializable:
function deepClone(value) {
  return JSON.parse(JSON.stringify(value));
}
Never build a JSON-like string by hand and feed it to eval() to "parse" it — that is a serious security hole if any part of the string came from an untrusted source. Always use JSON.parse().

Pass a number or whitespace string as `JSON.stringify()’s third argument to pretty-print the output (e.g. for a human-edited config file):

JSON.stringify({ s: "test", n: 0 }, null, 2);
// '{\n  "s": "test",\n  "n": 0\n}'

Customizing Serialization

If a value being stringified defines a toJSON() method, JSON.stringify() calls it and serializes the result in the value’s place — this is how Date participates in JSON despite not being natively supported: Date.prototype.toJSON() returns the same string as toISOString(). Round-tripping a Date through JSON therefore yields a plain ISO string on the other end, not a Date, unless you convert it back explicitly.

JSON.stringify()’s second argument can also be an array of property names to whitelist (and order), or a replacer function `(key, value) ⇒ newValue invoked for every value about to be serialized — returning undefined omits that property entirely:

// Only serialize these fields, in this order:
JSON.stringify(address, ["city", "state", "country"]);

// Drop any RegExp-valued property instead of erroring:
JSON.stringify(o, (key, value) => (value instanceof RegExp ? undefined : value));

JSON.parse()’s optional second argument is the inverse: a reviver function `(key, value) ⇒ newValue invoked once per primitive parsed from the input, useful for re-hydrating types JSON doesn’t support natively:

let data = JSON.parse(text, (key, value) => {
  if (key.startsWith("_")) return undefined;   // Drop private-ish fields
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);   // Re-create Date objects from ISO strings
  }
  return value;
});

Combining a toJSON()/replacer with a custom reviver effectively defines a private data format layered on top of JSON — convenient within a single codebase, but it trades away plug-and-play compatibility with the wider JSON tooling ecosystem, so use it deliberately.