Summary
Date.prototype.getUTCDate() returns the local day-of-month instead of the UTC one. The stored
time value is correct — only the getter is wrong — so this is invisible in any UTC environment,
including GitHub runners.
Minimal repro
const d = new Date(Date.UTC(2025, 5, 15));
console.log("iso0", d.toISOString());
d.setUTCDate(20);
console.log("iso1", d.toISOString());
console.log("getUTCDate", d.getUTCDate());
console.log("getDate", d.getDate());
console.log("getTime", d.getTime());
const e = new Date(Date.UTC(2025, 5, 20));
console.log("fresh_getUTCDate", e.getUTCDate());
TZ=America/Los_Angeles, pinned node v26.5.0:
| line |
perry |
node |
|
iso0 |
2025-06-15T00:00:00.000Z |
2025-06-15T00:00:00.000Z |
✅ |
iso1 |
2025-06-20T00:00:00.000Z |
2025-06-20T00:00:00.000Z |
✅ |
getTime |
1750377600000 |
1750377600000 |
✅ |
getDate |
19 |
19 |
✅ (correct local) |
getUTCDate |
19 |
20 |
❌ |
fresh_getUTCDate |
19 |
20 |
❌ |
fresh_getUTCDate uses a Date that was never mutated, so setUTCDate is not implicated — the
setter, the time value, and toISOString are all correct. getUTCDate is returning the same answer
as getDate, i.e. it appears to be reading local calendar fields.
Timezone dependence — why this has never been caught
| TZ |
result |
UTC |
test_gap_date_methods output matches node byte-for-byte |
America/Los_Angeles |
one line differs: 19 vs 20 |
CI runners are UTC, so the gap suite is green there and red on any developer machine west of
Greenwich. The failing line in test_gap_date_methods is console.log(d2.getUTCDate()); // 20.
Sibling getters were exercised in the same run and are correct under the same non-UTC TZ:
getUTCFullYear, getUTCMonth, getUTCHours, getUTCMinutes, getUTCSeconds, getUTCDay. So the
defect looks isolated to getUTCDate rather than a systematic UTC-getter problem — worth confirming
that during the fix, and worth checking getUTCMilliseconds which this test does not cover.
Not a regression from #6925
Found while sweeping PR #6925 (repsel Phase 5a, Ptr<Shape> proven this). Ruled out:
- A/B on the feature flag:
PERRY_PTR_SHAPE_THIS=0 vs default produce byte-identical output
(both wrong), so the representation change is not involved.
Date is a native builtin, not a user-declared class, so proven-this never routes its methods.
Suggested follow-up
Because the whole family is TZ-shadowed, consider running the gap suite under a non-UTC TZ in at
least one CI lane (or pinning TZ explicitly per test), otherwise this class of bug stays invisible
to CI by construction.
Summary
Date.prototype.getUTCDate()returns the local day-of-month instead of the UTC one. The storedtime value is correct — only the getter is wrong — so this is invisible in any UTC environment,
including GitHub runners.
Minimal repro
TZ=America/Los_Angeles, pinned node v26.5.0:iso02025-06-15T00:00:00.000Z2025-06-15T00:00:00.000Ziso12025-06-20T00:00:00.000Z2025-06-20T00:00:00.000ZgetTime17503776000001750377600000getDate1919getUTCDate1920fresh_getUTCDate1920fresh_getUTCDateuses a Date that was never mutated, sosetUTCDateis not implicated — thesetter, the time value, and
toISOStringare all correct.getUTCDateis returning the same answeras
getDate, i.e. it appears to be reading local calendar fields.Timezone dependence — why this has never been caught
UTCtest_gap_date_methodsoutput matches node byte-for-byteAmerica/Los_Angeles19vs20CI runners are UTC, so the gap suite is green there and red on any developer machine west of
Greenwich. The failing line in
test_gap_date_methodsisconsole.log(d2.getUTCDate()); // 20.Sibling getters were exercised in the same run and are correct under the same non-UTC TZ:
getUTCFullYear,getUTCMonth,getUTCHours,getUTCMinutes,getUTCSeconds,getUTCDay. So thedefect looks isolated to
getUTCDaterather than a systematic UTC-getter problem — worth confirmingthat during the fix, and worth checking
getUTCMillisecondswhich this test does not cover.Not a regression from #6925
Found while sweeping PR #6925 (repsel Phase 5a,
Ptr<Shape>proventhis). Ruled out:PERRY_PTR_SHAPE_THIS=0vs default produce byte-identical output(both wrong), so the representation change is not involved.
Dateis a native builtin, not a user-declared class, so proven-thisnever routes its methods.Suggested follow-up
Because the whole family is TZ-shadowed, consider running the gap suite under a non-UTC TZ in at
least one CI lane (or pinning
TZexplicitly per test), otherwise this class of bug stays invisibleto CI by construction.