diff options
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/DateConstructor.cpp | 64 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js | 1 |
2 files changed, 36 insertions, 29 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp index d453a8a4d2..4b87ba5a2c 100644 --- a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp @@ -25,7 +25,7 @@ static Value parse_simplified_iso8601(const String& iso_8601) // Date.parse() is allowed to accept many formats. We strictly only accept things matching // 21.4.1.15 Date Time String Format, https://tc39.es/ecma262/#sec-date-time-string-format GenericLexer lexer(iso_8601); - auto lex_n_digits = [&](size_t n, int& out) { + auto lex_n_digits = [&](size_t n, Optional<int>& out) { if (lexer.tell_remaining() < n) return false; int r = 0; @@ -39,36 +39,44 @@ static Value parse_simplified_iso8601(const String& iso_8601) return true; }; - int year = -1, month = -1, day = -1; - int hours = -1, minutes = -1, seconds = -1, milliseconds = -1; - int timezone = -1; - int timezone_hours = -1, timezone_minutes = -1; + Optional<int> year; + Optional<int> month; + Optional<int> day; + Optional<int> hours; + Optional<int> minutes; + Optional<int> seconds; + Optional<int> milliseconds; + Optional<char> timezone; + Optional<int> timezone_hours; + Optional<int> timezone_minutes; + auto lex_year = [&]() { if (lexer.consume_specific('+')) return lex_n_digits(6, year); if (lexer.consume_specific('-')) { - int absolute_year; + Optional<int> absolute_year; if (!lex_n_digits(6, absolute_year)) return false; - year = -absolute_year; + year = -absolute_year.value(); return true; } return lex_n_digits(4, year); }; - auto lex_month = [&]() { return lex_n_digits(2, month) && month >= 1 && month <= 12; }; - auto lex_day = [&]() { return lex_n_digits(2, day) && day >= 1 && day <= 31; }; + auto lex_month = [&]() { return lex_n_digits(2, month) && *month >= 1 && *month <= 12; }; + auto lex_day = [&]() { return lex_n_digits(2, day) && *day >= 1 && *day <= 31; }; auto lex_date = [&]() { return lex_year() && (!lexer.consume_specific('-') || (lex_month() && (!lexer.consume_specific('-') || lex_day()))); }; - auto lex_hours_minutes = [&](int& out_h, int& out_m) { - int h, m; - if (lex_n_digits(2, h) && h >= 0 && h <= 24 && lexer.consume_specific(':') && lex_n_digits(2, m) && m >= 0 && m <= 59) { - out_h = h; - out_m = m; + auto lex_hours_minutes = [&](Optional<int>& out_h, Optional<int>& out_m) { + Optional<int> h; + Optional<int> m; + if (lex_n_digits(2, h) && *h >= 0 && *h <= 24 && lexer.consume_specific(':') && lex_n_digits(2, m) && *m >= 0 && *m <= 59) { + out_h = move(h); + out_m = move(m); return true; } return false; }; - auto lex_seconds = [&]() { return lex_n_digits(2, seconds) && seconds >= 0 && seconds <= 59; }; + auto lex_seconds = [&]() { return lex_n_digits(2, seconds) && *seconds >= 0 && *seconds <= 59; }; auto lex_milliseconds = [&]() { return lex_n_digits(3, milliseconds); }; auto lex_seconds_milliseconds = [&]() { return lex_seconds() && (!lexer.consume_specific('.') || lex_milliseconds()); }; auto lex_timezone = [&]() { @@ -90,34 +98,32 @@ static Value parse_simplified_iso8601(const String& iso_8601) return js_nan(); } - // We parsed a valid date simplified ISO 8601 string. Values not present in the string are -1. - VERIFY(year != -1); // A valid date string always has at least a year. + // We parsed a valid date simplified ISO 8601 string. + VERIFY(year.has_value()); // A valid date string always has at least a year. struct tm tm = {}; - tm.tm_year = year - 1900; - tm.tm_mon = month == -1 ? 0 : month - 1; - tm.tm_mday = day == -1 ? 1 : day; - tm.tm_hour = hours == -1 ? 0 : hours; - tm.tm_min = minutes == -1 ? 0 : minutes; - tm.tm_sec = seconds == -1 ? 0 : seconds; + tm.tm_year = *year - 1900; + tm.tm_mon = !month.has_value() ? 0 : *month - 1; + tm.tm_mday = day.value_or(1); + tm.tm_hour = hours.value_or(0); + tm.tm_min = minutes.value_or(0); + tm.tm_sec = seconds.value_or(0); // https://tc39.es/ecma262/#sec-date.parse: // "When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time." time_t timestamp; - if (timezone != -1 || hours == -1) + if (timezone.has_value() || !hours.has_value()) timestamp = timegm(&tm); else timestamp = mktime(&tm); if (timezone == '-') - timestamp += (timezone_hours * 60 + timezone_minutes) * 60; + timestamp += (*timezone_hours * 60 + *timezone_minutes) * 60; else if (timezone == '+') - timestamp -= (timezone_hours * 60 + timezone_minutes) * 60; + timestamp -= (*timezone_hours * 60 + *timezone_minutes) * 60; // FIXME: reject timestamp if resulting value wouldn't fit in a double - if (milliseconds == -1) - milliseconds = 0; - return Value(1000.0 * timestamp + milliseconds); + return Value(1000.0 * timestamp + milliseconds.value_or(0)); } DateConstructor::DateConstructor(GlobalObject& global_object) diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js index ca2766ae34..e5fcf7e0f6 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js @@ -10,6 +10,7 @@ test("basic functionality", () => { expect(Date.parse("2020T23:59Z")).toBe(1577923140000); expect(Date.parse("+020000")).toBe(568971820800000); + expect(Date.parse("-000001")).toBe(-62198755200000); expect(Date.parse("+020000-01")).toBe(568971820800000); expect(Date.parse("+020000-01T00:00:00.000Z")).toBe(568971820800000); |