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 |
|---|---|
|
Local time zone, not locale-aware. |
|
UTC time zone, not locale-aware. |
|
ISO-8601 ( |
|
Local time zone, locale-appropriate format (date + time). |
|
Date portion only; the |
|
Time portion only; the |
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.