1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 00:47:45 +00:00

LibJS: Implement Date.UTC according to the spec

This fixes all failing Date.UTC test262 tests, which failed due to not
handling invalid input and evaluating inputs out of order. But this also
avoids using timegm(), which doesn't work on macOS for years before 1900
(they simply return -1 for those years).

Partially addresses #4651. Date.parse.js still fails.
This commit is contained in:
Timothy Flynn 2022-01-05 11:19:17 -05:00 committed by Linus Groh
parent 7a0830bb24
commit 260d2099da
2 changed files with 48 additions and 15 deletions

View file

@ -302,24 +302,40 @@ JS_DEFINE_NATIVE_FUNCTION(DateConstructor::parse)
// 21.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ), https://tc39.es/ecma262/#sec-date.utc // 21.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ), https://tc39.es/ecma262/#sec-date.utc
JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc) JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc)
{ {
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<i32> { auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
return vm.argument_count() > i ? vm.argument(i).to_i32(global_object) : fallback; return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
}; };
int year = TRY(vm.argument(0).to_i32(global_object));
if (year >= 0 && year <= 99)
year += 1900;
struct tm tm = {}; // 1. Let y be ? ToNumber(year).
tm.tm_year = year - 1900; auto year = TRY(vm.argument(0).to_number(global_object));
tm.tm_mon = TRY(arg_or(1, 0)); // 0-based in both tm and JavaScript // 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽.
tm.tm_mday = TRY(arg_or(2, 1)); auto month = TRY(arg_or(1, 0));
tm.tm_hour = TRY(arg_or(3, 0)); // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽.
tm.tm_min = TRY(arg_or(4, 0)); auto date = TRY(arg_or(2, 1));
tm.tm_sec = TRY(arg_or(5, 0)); // 4. If hours is present, let h be ? ToNumber(hours); else let h be +0𝔽.
// timegm() doesn't read tm.tm_wday and tm.tm_yday, no need to fill them in. auto hours = TRY(arg_or(3, 0));
// 5. If minutes is present, let min be ? ToNumber(minutes); else let min be +0𝔽.
auto minutes = TRY(arg_or(4, 0));
// 6. If seconds is present, let s be ? ToNumber(seconds); else let s be +0𝔽.
auto seconds = TRY(arg_or(5, 0));
// 7. If ms is present, let milli be ? ToNumber(ms); else let milli be +0𝔽.
auto milliseconds = TRY(arg_or(6, 0));
int milliseconds = TRY(arg_or(6, 0)); // 8. If y is NaN, let yr be NaN.
return Value(1000.0 * timegm(&tm) + milliseconds); // 9. Else,
if (!year.is_nan()) {
// a. Let yi be ! ToIntegerOrInfinity(y).
auto year_double = MUST(year.to_integer_or_infinity(global_object));
// b. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y.
if (0 <= year_double && year_double <= 99)
year = Value(1900 + year_double);
}
// 10. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))).
auto day = make_day(global_object, year, month, date);
auto time = make_time(global_object, hours, minutes, seconds, milliseconds);
return time_clip(global_object, make_date(day, time));
} }
} }

View file

@ -53,3 +53,20 @@ test("out of range", () => {
expect(Date.UTC(2020, 1, 15, 12, 30, 30, -2345)).toBe(1581769827655); expect(Date.UTC(2020, 1, 15, 12, 30, 30, -2345)).toBe(1581769827655);
expect(Date.UTC(2020, 1, 15, 12, 30, 30, 2345)).toBe(1581769832345); expect(Date.UTC(2020, 1, 15, 12, 30, 30, 2345)).toBe(1581769832345);
}); });
test("special values", () => {
[Infinity, -Infinity, NaN].forEach(value => {
expect(Date.UTC(value)).toBeNaN();
expect(Date.UTC(0, value)).toBeNaN();
expect(Date.UTC(0, 0, value)).toBeNaN();
expect(Date.UTC(0, 0, 1, value)).toBeNaN();
expect(Date.UTC(0, 0, 1, 0, value)).toBeNaN();
expect(Date.UTC(0, 0, 1, 0, 0, value)).toBeNaN();
expect(Date.UTC(0, 0, 1, 0, 0, 0, value)).toBeNaN();
});
});
test("time clip", () => {
expect(Date.UTC(275760, 8, 13, 0, 0, 0, 0)).toBe(8.64e15);
expect(Date.UTC(275760, 8, 13, 0, 0, 0, 1)).toBeNaN();
});