From 5f595e7e1b15791d32beb9737c3c0cf4fa7cbf38 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sat, 22 Aug 2020 22:15:00 -0400 Subject: [PATCH] LibC: Make localtime() and gmtime() handle years before 1970 Year computation has to be based on seconds, not days, in case t is < 0 but t / __seconds_per_day is 0. Year computation also has to consider negative timestamps. With this, days is always positive and <= the number of days in the year, so base the tm_wday computation directly on the timestamp, and do it first, before t is modified in the year computation. In C, % can return a negative number if the left operand is negative, compensate for that. Tested via test-js. (Except for tm_wday, since we don't implement Date.prototype.getUTCDate() yet.) --- Libraries/LibC/time.cpp | 16 ++++++++++++---- .../builtins/Date/Date.prototype.toISOString.js | 10 ++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Libraries/LibC/time.cpp b/Libraries/LibC/time.cpp index e06c79a6a7..6b1d0239a0 100644 --- a/Libraries/LibC/time.cpp +++ b/Libraries/LibC/time.cpp @@ -69,16 +69,24 @@ static const int __seconds_per_day = 60 * 60 * 24; static void time_to_tm(struct tm* tm, time_t t) { + tm->tm_wday = (4 * __seconds_per_day + t) % (7 * __seconds_per_day); // 1970-01-01 was a Thursday. + if (tm->tm_wday < 0) + tm->tm_wday += 7 * __seconds_per_day; + tm->tm_wday /= __seconds_per_day; + + int year = 1970; + for (; t >= (365 + __is_leap_year(year)) * __seconds_per_day; ++year) + t -= (365 + __is_leap_year(year)) * __seconds_per_day; + for (; t < 0; --year) + t += (365 + __is_leap_year(year - 1)) * __seconds_per_day; + ASSERT(t >= 0); + int days = t / __seconds_per_day; int remaining = t % __seconds_per_day; tm->tm_sec = remaining % 60; remaining /= 60; tm->tm_min = remaining % 60; tm->tm_hour = remaining / 60; - tm->tm_wday = (4 + days) % 7; - int year; - for (year = 1970; days >= 365 + __is_leap_year(year); ++year) - days -= 365 + __is_leap_year(year); tm->tm_year = year - 1900; tm->tm_yday = days; tm->tm_mday = 1; diff --git a/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toISOString.js b/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toISOString.js index f929540f99..2b380fc24d 100644 --- a/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toISOString.js +++ b/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toISOString.js @@ -1,9 +1,7 @@ test("basic functionality", () => { expect(new Date(1597955034555).toISOString()).toBe("2020-08-20T20:23:54.555Z"); - - // FIXME: Add these once they work. - //expect(new Date(Date.UTC(22020)).toISOString()).toBe("+022020-01-01T00:00:00.000Z"); - //expect(new Date(Date.UTC(1950)).toISOString()).toBe("1950-01-01T00:00:00.000Z"); - //expect(new Date(Date.UTC(1800)).toISOString()).toBe("1800-01-01T00:00:00.000Z"); - //expect(new Date(Date.UTC(-100)).toISOString()).toBe("-000100-01-01T00:00:00.000Z"); + expect(new Date(Date.UTC(22020)).toISOString()).toBe("+022020-01-01T00:00:00.000Z"); + expect(new Date(Date.UTC(1950)).toISOString()).toBe("1950-01-01T00:00:00.000Z"); + expect(new Date(Date.UTC(1800)).toISOString()).toBe("1800-01-01T00:00:00.000Z"); + expect(new Date(Date.UTC(-100)).toISOString()).toBe("-000100-01-01T00:00:00.000Z"); });