Skip to content

Date.prototype.getUTCDate returns the local day-of-month (TZ-dependent; invisible on UTC CI runners) #6967

Description

@proggeramlug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions