diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp index f465c89391..34b78ffa2b 100644 --- a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp @@ -79,7 +79,33 @@ static Value parse_simplified_iso8601(GlobalObject& global_object, const String& return false; }; auto lex_seconds = [&]() { return lex_n_digits(2, seconds) && *seconds >= 0 && *seconds <= 59; }; - auto lex_milliseconds = [&]() { return lex_n_digits(3, milliseconds); }; + auto lex_milliseconds = [&]() { + // Date.parse() is allowed to accept an arbitrary number of implementation-defined formats. + // Milliseconds are parsed slightly different as other engines allow effectively any number of digits here. + // We require at least one digit and only use the first three. + + auto digits_read = 0; + int result = 0; + while (!lexer.is_eof() && is_ascii_digit(lexer.peek())) { + char ch = lexer.consume(); + if (digits_read < 3) + result = 10 * result + ch - '0'; + + ++digits_read; + } + + if (digits_read == 0) + return false; + + // If we got less than three digits pretend we have trailing zeros. + while (digits_read < 3) { + result *= 10; + ++digits_read; + } + + milliseconds = result; + return true; + }; auto lex_seconds_milliseconds = [&]() { return lex_seconds() && (!lexer.consume_specific('.') || lex_milliseconds()); }; auto lex_timezone = [&]() { if (lexer.consume_specific('+')) { diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js index 6b42f7d829..3ace60e830 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.parse.js @@ -36,3 +36,29 @@ test("time clip", () => { expect(Date.parse("+999999")).toBeNaN(); expect(Date.parse("-999999")).toBeNaN(); }); + +test("extra micro seconds extension", () => { + expect(Date.parse("2021-04-30T15:19:02.937+00:00")).toBe(1619795942937); + expect(Date.parse("2021-04-30T15:19:02.9370+00:00")).toBe(1619795942937); + expect(Date.parse("2021-04-30T15:19:02.93700+00:00")).toBe(1619795942937); + expect(Date.parse("2021-04-30T15:19:02.937000+00:00")).toBe(1619795942937); + + expect(Date.parse("2021-04-30T15:19:02.93+00:00")).toBe(1619795942930); + expect(Date.parse("2021-04-30T15:19:02.9+00:00")).toBe(1619795942900); + + // These values are just checked against NaN since they don't have a specified timezone. + expect(Date.parse("2021-04-30T15:19:02.93")).not.toBe(NaN); + expect(Date.parse("2021-04-30T15:19:02.9")).not.toBe(NaN); + + expect(Date.parse("2021-04-30T15:19:02.+00:00")).toBe(NaN); + expect(Date.parse("2021-04-30T15:19:02.")).toBe(NaN); + expect(Date.parse("2021-04-30T15:19:02.a")).toBe(NaN); + expect(Date.parse("2021-04-30T15:19:02.000a")).toBe(NaN); + + expect(Date.parse("2021-04-30T15:19:02.937001+00:00")).toBe(1619795942937); + expect(Date.parse("2021-04-30T15:19:02.937999+00:00")).toBe(1619795942937); + + expect(Date.parse("2021-06-26T07:24:40.007000+00:00")).toBe(1624692280007); + expect(Date.parse("2021-06-26T07:24:40.0079999999999999999+00:00")).toBe(1624692280007); + expect(Date.parse("2021-04-15T18:47:25.606000+00:00")).toBe(1618512445606); +});