Lesson 48 of 80
Date is the API JavaScript developers trust least, for good reason — it mutates in place, parses strings inconsistently, and silently assumes a timezone you didn't pick.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A Date object is really just a wrapper around a single number: milliseconds since the Unix epoch (Jan 1, 1970 UTC). Everything it does — display, arithmetic, comparison — happens through that one internal timestamp. The pitfalls all come from two design choices made decades ago: Date methods mutate the object in place instead of returning a new one, and every get/set method (getDate, getHours, setMonth, ...) operates in the browser's local timezone unless you explicitly reach for the UTC variant.
Neither choice is wrong exactly — they're just easy to forget, and forgetting either one produces bugs that only show up for users in a different timezone than the one you tested in, which is exactly why they slip past code review.
function addOneWeek(date) {
date.setDate(date.getDate() + 7); // mutates `date` in place
return date;
}
const deadline = new Date("2026-03-01");
const reminder = addOneWeek(deadline);
deadline === reminder; // true — same object!
deadline; // also March 8th now — the "original" was mutated too
Every caller that still holds deadline after this call sees it silently moved a week forward, because setDate doesn't return a new Date — it mutates the receiver and returns the new timestamp as a number, which this code ignores. The fix is the same shape as any other JS mutation bug: copy first, mutate the copy.
function addOneWeek(date) {
const copy = new Date(date); // new Date from an existing Date copies its timestamp
copy.setDate(copy.getDate() + 7);
return copy;
}
Now the timezone trap, which is more subtle because it looks correct in whichever timezone you're testing from:
const d = new Date("2026-03-01"); // parsed as UTC midnight — the date-only ISO format is special-cased to UTC
d.getUTCHours(); // 0 — midnight UTC, as expected
d.getHours(); // could be 19 (the previous day, in US Eastern) or 5 (the next day, in India) — LOCAL timezone
d.getDate(); // 1 in UTC-adjacent zones, but 28 (February!) somewhere west of UTC
new Date("2026-03-01") (a date-only ISO string) always parses as UTC midnight. But getDate(), getMonth(), getHours() — the plain, no-UTC methods — report those fields as seen in whatever timezone the running machine is set to. Parse in UTC, then read in local time, and the calendar date you get back can be a full day off from the one you typed, depending on where the code happens to run.
new Date("2026-03-01") and new Date(2026, 2, 1) parse completely differently. The ISO string form is UTC; the numeric-arguments form (note: month is 0-indexed, so 2 means March) is local time. Passing what looks like "the same date" through each constructor can produce two Date objects that are hours apart.toSorted/with pair, Date has no withDate/withMonth alternative — setDate, setMonth, setHours, and friends all mutate the receiver. Cloning with new Date(existingDate) before mutating is the only reliable workaround.== or < doesn't do what people expect, and === never works for equality. date1 === date2 is always false for two different Date objects even at the identical timestamp, since it's a reference comparison. </> do coerce to the numeric timestamp and work correctly for ordering, but equality needs date1.getTime() === date2.getTime().This lesson is about Date's own arithmetic and timezone behavior — the Intl formatting lesson covers a different problem entirely: turning a timestamp into a string a specific locale expects to read (comma placement, month names, 12- vs 24-hour clocks). Intl.DateTimeFormat takes a Date as input and never mutates it, which sidesteps every pitfall on this page — but it doesn't help you compute "next Tuesday" correctly. You need both: this lesson for correct arithmetic, Intl for correct display.