Date.prototype.setFullYear()
Sets the year of a date in local time, optionally the month and day too, and returns the new timestamp.
setFullYear(yearValue)
setFullYear(yearValue, monthValue)
setFullYear(yearValue, monthValue, dateValue)- Added in JavaScript ES1
Parameters
| Parameter | Type | Description |
|---|---|---|
$yearValue | number | An integer representing the year, for example 1995. Values outside the usual range shift the date accordingly. |
$monthValueoptional | number= this.getMonth() | An integer representing the month, 0 for January through 11 for December. Defaults to the value returned by getMonth(). |
$dateValueoptional | number= this.getDate() | An integer between 1 and 31 representing the day of the month. If specified, monthValue must be specified too. Defaults to the value returned by getDate(). |
Return value
numberThe new timestamp of the modified Date object in milliseconds since the epoch. Returns NaN if any parameter is NaN or coerces to NaN, in which case the Date is set to Invalid Date.
Changelog
- PHP Modern enginesWhen the resulting local time lands in a DST gap or ambiguity, engines resolve it the way Temporal's "compatible" disambiguation does: the earlier instant for ambiguous times, and gaps are crossed forward.
setFullYear() changes the year on an existing Date in place and returns the resulting timestamp — not the date object. Assigning the return value to a variable gives you a number, which is the mistake people make once and never forget.
Set several fields at once
Passing month and day in the same call avoids an intermediate invalid state. Setting the year first and the month second can pass through a date that does not exist, and the optional arguments sidestep that entirely.
What trips people up
Out-of-range values do not throw, they roll: a monthValue of 15 means March of the following year. That is useful for date maths and surprising when it comes from unchecked input.
The leap-day case catches everyone. Take 29 February 2024, call setFullYear(2023), and you get 1 March 2023 — because 29 February 2023 does not exist and the overflow rolls forward. Anniversary and renewal logic must handle this explicitly.
Finally, this is a local-time operation, so moving a date across a DST boundary changes the elapsed milliseconds by an hour. For fixed-size shifts use setUTCFullYear() or setTime().
Example
When to use it
- Roll a subscription renewal date forward by one year
- Normalize a batch of imported dates onto a single reference year
- Set year, month and day in one call when rebuilding a date from form fields
- Compute someone's birthday in the current year
Frequently asked questions
Why does setFullYear() return a number instead of the Date?
All of the Date setters return the new timestamp in milliseconds and mutate the receiver in place, so they cannot be chained. Call the setter as a statement and keep using the original variable, or clone with new Date(d) first if you need the old value.
What happens to 29 February when I change the year?
It rolls forward to 1 March if the target year is not a leap year, because 29 February does not exist there and out-of-range days overflow into the next month. Clamp the day against the target month's real length if you need it pinned to February.
How do I set the year without changing the time?
setFullYear() already leaves the hours, minutes, seconds and milliseconds alone — only the year, and optionally the month and day, change. If the date crosses a daylight saving boundary, though, the wall-clock time is preserved while the elapsed milliseconds shift by an hour.
More examples
The leap day trap
The first log shows the silent rollover into March. The helper avoids it by setting day 1 first, measuring the target month's real length, then clamping — the pattern any renewal or anniversary feature needs.
const leap = new Date(2024, 1, 29); // 29 Feb 2024
leap.setFullYear(2023);
console.log(leap.toDateString()); // Wed Mar 01 2023 — Feb 29 does not exist
// Clamp instead of rolling over
function sameDayNextYear(d) {
const out = new Date(d);
out.setFullYear(d.getFullYear() + 1, d.getMonth(), 1);
const lastDay = new Date(out.getFullYear(), out.getMonth() + 1, 0).getDate();
out.setDate(Math.min(d.getDate(), lastDay));
return out;
}
console.log(sameDayNextYear(new Date(2024, 1, 29)).toDateString()); // Wed Feb 28 2025 — clamped, not rolled overMutation is shared, so clone first
Assigning a Date to another variable copies the reference, so the setter changes both names. Passing the date through new Date(d) first is the cheapest way to get an independent copy.
const original = new Date(2024, 0, 15);
const alias = original;
alias.setFullYear(2030);
console.log(original.getFullYear()); // 2030 — same object
const safe = new Date(original);
safe.setFullYear(1999);
console.log(original.getFullYear(), safe.getFullYear()); // 2030 1999
Discussion