Repository navigation
runtime: match Date.UTC defaults, year rebasing, and invalid-argument semantics #2826
Copy link
Copy link
Closed
Description
Summary
Date.UTC is implemented, but its argument normalization does not match Node for important edge cases:
- missing
daydefaults to1, not0; - no arguments should return
NaNbecause the requiredyearisundefined; - years
0..99are interpreted as1900 + year; - component overflow/underflow should be normalized by the Date algorithm.
Perry currently pads every missing argument with 0 and casts components directly before calling components_to_timestamp.
Expected Node behavior
Local Node v25.9.0 probe:
Date.UTC(); // NaN
Date.UTC(2020); // 1577836800000 (2020-01-01T00:00:00.000Z)
Date.UTC(2020, 0); // 1577836800000
Date.UTC(2020, 0, 1); // 1577836800000
Date.UTC(2020, 0, 0); // 1577750400000 (2019-12-31T00:00:00.000Z)
Date.UTC(0, 0, 1); // -2208988800000 (1900-01-01T00:00:00.000Z)
Date.UTC(99, 0, 1); // 915148800000 (1999-01-01T00:00:00.000Z)
Date.UTC(100, 0, 1); // -59011459200000 (0100-01-01T00:00:00.000Z)
Date.UTC(2020, 12, 1); // 1609459200000 (2021-01-01T00:00:00.000Z)Current Perry implementation
Source evidence from current tree:
crates/perry-hir/src/lower/expr_call/module_static.rslowers anyDate.UTC(...)call toExpr::DateUtc(args).crates/perry-codegen/src/expr/misc_methods.rslowers up to seven args and pads every missing slot withdouble_literal(0.0). That makesDate.UTC()andDate.UTC(2020)indistinguishable from explicit zeroes for missing components, but Node treats missingyearasNaNand missingdayas1.crates/perry-runtime/src/date.rs::js_date_utc()castsyear as i32, convertsmonth + 1tou32, castsday/hour/minute/secondto unsigned integers, and passes them tocomponents_to_timestamp.js_date_utc()does not apply the0..99 => 1900 + yearrule that the adjacentjs_date_new_local_components()implementation documents for multi-argumentnew Date(...).- The unsigned casts make negative/overflow component normalization unlikely to match Node's date construction semantics for cases such as day
0, negative days/months, and month overflow.
Suggested test surface
Add parity tests for the edge cases above:
assert.equal(Number.isNaN(Date.UTC()), true);
assert.equal(Date.UTC(2020), Date.UTC(2020, 0, 1));
assert.equal(Date.UTC(2020, 0), Date.UTC(2020, 0, 1));
assert.equal(Date.UTC(2020, 0, 0), Date.parse("2019-12-31T00:00:00.000Z"));
assert.equal(Date.UTC(0, 0, 1), Date.parse("1900-01-01T00:00:00.000Z"));
assert.equal(Date.UTC(99, 0, 1), Date.parse("1999-01-01T00:00:00.000Z"));
assert.equal(Date.UTC(100, 0, 1), Date.parse("0100-01-01T00:00:00.000Z"));
assert.equal(Date.UTC(2020, 12, 1), Date.parse("2021-01-01T00:00:00.000Z"));Scope / non-goals
This is not the older Date corruption work around #748/#858/#859 or the Date reference/mutation fix #2089. It is specifically about the static Date.UTC argument defaulting and normalization contract.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity