Handling time is where people get caught out most often, in every language. JavaScript adds two quirks of its own: months numbered from zero, and a time zone conversion that fires without warning.
Once those two points are understood, the Date object becomes predictable, and formatting is delegated entirely to the browser internationalization interface.
Definition
A Date object represents a single moment, stored internally as a number of milliseconds elapsed since January 1st 1970 at midnight UTC. Every reading method is just a different way of presenting that same number.
const d = new Date("2026-08-20T10:00:00Z");
console.log(d.getFullYear()); // 2026
console.log(d.getUTCMonth()); // 7 and not 8: months start at 0
console.log(d.getUTCDate()); // 20
console.log(d.toISOString()); // "2026-08-20T10:00:00.000Z"
console.log(typeof d.getTime()); // "number"The new operator is mandatory. Without it, Date() returns a string instead of an object, which breaks everything downstream with no error message.
Months run from 0 to 11, but days of the month from 1 to 31. That inconsistency dates back to the origins of the language and will never be fixed. getDay(), for its part, returns the weekday, with 0 for Sunday.
The time zone trap
How the date is written at creation time changes how it is interpreted.
| Written as | Interpreted as |
|---|---|
"2026-08-20" | Midnight UTC |
"2026-08-20T10:00:00Z" | 10am UTC, unambiguous |
new Date(2026, 7, 20) | Midnight in the local zone |
The first line is the surprising one: a bare date is read as UTC, then displayed in the local zone. In New York it moves back a day as soon as it is printed with getDate(). The safe rule is to store in UTC and convert only when displaying.
Formatting and calculating
Never assemble a human-readable date by hand. Intl.DateTimeFormat knows the languages, the day-month orders and the month names.
const d = new Date("2026-08-20T10:00:00Z");
console.log(d.toLocaleDateString("en-US", {
timeZone: "UTC", weekday: "long", day: "numeric", month: "long", year: "numeric"
}));
// "Thursday, August 20, 2026"
const start = new Date("2026-01-01T00:00:00Z");
const end = new Date("2026-01-08T00:00:00Z");
console.log((end - start) / 86400000); // 7 daysThe subtraction works because both objects convert to a number of milliseconds, a case of Coercion (type conversion) that for once does you a favor. Dividing by 86,400,000 gives a number of days.
Frequently asked questions
How do I get today's date?
new Date() with no argument returns the current moment, and Date.now() returns that same moment directly in milliseconds. The second form is preferable for measuring a duration, since it avoids creating an object when only one number is needed.
How do I add days to a date?
The simplest route is setDate(d.getDate() + 7), which handles month boundaries and leap years on its own. Careful though, this method modifies the existing object rather than creating a new one: copy the date first if the original must stay intact.
Do I need a library to handle dates?
Less and less. Intl covers formatting, which was the main need, and the Temporal API is progressively landing in browsers to handle zones and durations cleanly. A library still earns its place for parsing varied formats or advanced calendar arithmetic, much less so for displaying a date.