Date setters (setMonth, setDate, setHours…)
Three surprises in one family: they change the object you called them on, they hand back a number instead of the object, and an out-of-range value rolls over instead of failing.
Demo
The first case is the classic month-arithmetic bug. Setting the month of 31 January to February asks for 31 February, which does not exist — so it rolls forward three days to 3 March. Nothing warns you, and the result is a valid date in the wrong month. The third case shows the same mechanism upward: month 12 is out of the 0–11 range, so it becomes January of the following year. That rollover is occasionally useful and far more often the reason a recurring-date calculation drifts.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| value | number | yes | The new component value. Out-of-range values are not rejected — they roll forward or backward, so setMonth(12) advances the year and setDate(0) goes to the last day of the previous month. |
Return value
number — The new timestamp in milliseconds — NOT the Date object. They mutate the Date in place, so chaining does not work.
Common patterns
const d = new Date(start); d.setDate(d.getDate() + 7);
const d = new Date(start); const day = d.getDate(); d.setDate(1); d.setMonth(d.getMonth() + 1); d.setDate(Math.min(day, daysInMonth(d)));
const d = new Date(); d.setHours(0); d.setMinutes(0); // separate statements
Examples
Pitfalls
const d = new Date(Date.UTC(2026, 0, 31)); d.setUTCMonth(d.getUTCMonth() + 1); d.toISOString().slice(0, 10)
const d = new Date(Date.UTC(2026, 0, 31)); d.setUTCDate(1); d.setUTCMonth(1); d.toISOString().slice(0, 10)
new Date().setHours(0).setMinutes(0)
const d = new Date(); d.setHours(0); d.setMinutes(0);
function startOfDay(d) { d.setHours(0, 0, 0, 0); return d; }
function startOfDay(d) { const c = new Date(d); c.setHours(0, 0, 0, 0); return c; }
const d = new Date(Date.UTC(2024, 1, 29)); d.setUTCFullYear(2026); d.toISOString().slice(0, 10)
const d = new Date(Date.UTC(2026, 1, Math.min(29, 28)));
When to use
- Adding or subtracting days, where rollover is what you want
- Zeroing out the time part to get the start of a day
- setTime to jump to an absolute timestamp
- Month arithmetic on month-end dates → clamp explicitly, or use a library
- Chaining → each call returns a number
- A Date you did not create → copy it first
- Anything a library like Temporal or date-fns does immutably
Notes
FAQ
Set the day to 1 first, change the month, then set the day to the smaller of the original day and the new month length. Without the clamp, month-end dates skip a month. This is the single best argument for a date library.
function addMonth(d) { const day = d.getDate(); const c = new Date(d); c.setDate(1); c.setMonth(c.getMonth() + 1); const last = new Date(c.getFullYear(), c.getMonth() + 1, 0).getDate(); c.setDate(Math.min(day, last)); return c; }