Browse Source

LibJS/Date: Ensure `YearFromTime(t)` holds invariant after approximation

As of https://tc39.es/ecma262/#sec-yearfromtime, YearFromTime(t) should
return `y` such that `TimeFromYear(YearFromTime(t)) <= t`. This wasn't
held, since the approximation contained decimal digits that would nudge
the final value in the wrong direction.

Adapted from Kiesel:
https://codeberg.org/kiesel-js/kiesel/commit/6548a857439ca175d4254c880442bb5267e92a04

Co-authored-by: Linus Groh <mail@linusgroh.de>
Jesús (gsus) Lapastora 1 year ago
parent
commit
2086b8df9c

+ 1 - 1
Userland/Libraries/LibJS/Runtime/Date.cpp

@@ -138,7 +138,7 @@ i32 year_from_time(double t)
         return NumericLimits<i32>::max();
 
     // Approximation using average number of milliseconds per year. We might have to adjust this guess afterwards.
-    auto year = static_cast<i32>(t / (365.2425 * ms_per_day) + 1970);
+    auto year = static_cast<i32>(floor(t / (365.2425 * ms_per_day) + 1970));
 
     auto year_t = time_from_year(year);
     if (year_t > t)

+ 9 - 0
Userland/Libraries/LibJS/Tests/builtins/Date/Date.UTC.js

@@ -70,3 +70,12 @@ 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();
 });
+
+test("YearFromTime invariant holds with negative times", () => {
+    // https://tc39.es/ecma262/#sec-yearfromtime: YearFromTime(t) should return
+    // a value such that TimeFromYear(YearFromTime(t)) <= t.
+    //
+    // If this doesn't hold, then the following Date constructor will result in
+    // a crash from an assertion (#21548).
+    new Date(Date.UTC(-1112, 11, 31));
+});