Skip to content

fix(date): NaN-revive setters seed from raw +0, not LocalTime(0) (#4927)#5055

Merged
proggeramlug merged 1 commit into
mainfrom
fix/date-setter-dst-offset
Jun 13, 2026
Merged

fix(date): NaN-revive setters seed from raw +0, not LocalTime(0) (#4927)#5055
proggeramlug merged 1 commit into
mainfrom
fix/date-setter-dst-offset

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Fixes #4927.

Root cause

Narrower than the issue's suspicion: the local→UTC conversion in rebuild_local_with already computes the offset at the target instant, and DST-crossing setters on valid dates were already correct. The actual bug was the Invalid-Date revive path: ECMA-262 Date.prototype.setFullYear step 2 substitutes t = +0 for a NaN time value without applying LocalTime — the seed components are raw UTC +0 (1970-01-01 00:00:00). js_date_apply_setter substituted base = 0.0 before calling the rebuild, which then decoded the epoch through timestamp_to_local_components, picking up the 1970 epoch's local offset: CET revived new Date(NaN).setFullYear(2020) to local 01:00; TZ=America/New_York landed on 2020-12-31 19:00. TZ=UTC (CI) was unaffected — exactly the reported green-CI/red-laptop split.

Fix

Pass the NaN time value through to rebuild_with/rebuild_local_with — both already implement the spec's NaN branch (seed 1970/0/1 00:00:00, revive only when a year override is present, reassemble as local + offset-at-target). One substitution line removed.

Validation

  • date::tests::test_full_year_setters_revive_invalid_date_only now passes under TZ=UTC, America/New_York, Australia/Sydney, Europe/Amsterdam (previously failed everywhere but UTC); full date:: group green under all four.
  • Compiled e2e matrix vs node --experimental-strip-types, byte-identical in all four TZs: NaN revive (local + UTC), setMonth across DST boundaries both directions, setFullYear from a year-end timestamp, local constructors in Jan/Jul (getTime checked), setHours across the year.

Code-only PR — version bump + changelog left for merge time.

Date.prototype.setFullYear step 2 substitutes t = +0 for a NaN time
value WITHOUT applying LocalTime — the seed components are the raw UTC
+0 reading (1970-01-01 00:00:00). js_date_apply_setter substituted 0.0
before calling rebuild_local_with, which then decoded the epoch through
timestamp_to_local_components, picking up the 1970 epoch's local offset:
a CET process revived new Date(NaN).setFullYear(2020) to local 01:00
(test expected 00:00), and TZ=America/New_York landed on Dec 31 19:00.
CI (TZ=UTC) never saw it.

Pass the NaN through instead — both rebuild_with and rebuild_local_with
already implement the spec's NaN branch (seed 1970/0/1 00:00:00, revive
only when a year override is present).
@proggeramlug proggeramlug merged commit d00018d into main Jun 13, 2026
13 checks passed
@proggeramlug proggeramlug deleted the fix/date-setter-dst-offset branch June 13, 2026 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Date local setters use the current-time tz offset instead of the target-date offset (DST off-by-one-hour; TZ-dependent unit test)

1 participant