From 4567ded8e4ccab7ab7677860bd68e54c19ecc71c Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Mon, 17 Oct 2022 09:20:38 +0200 Subject: [PATCH] LibJS: Reject relativeTo string such as "2022-08-18T17:01Z" This is a normative change in the Temporal spec. See: https://github.com/tc39/proposal-temporal/commit/2dc20bf --- Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 2 ++ .../LibJS/Runtime/Temporal/AbstractOperations.cpp | 10 ++++++++-- Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp | 1 + Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h | 1 + .../builtins/Temporal/Duration/Duration.compare.js | 10 ++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index b2190caf7a..cb7cb43b33 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -254,6 +254,8 @@ M(TemporalInvalidPlainMonthDay, "Invalid plain month day") \ M(TemporalInvalidPlainTime, "Invalid plain time") \ M(TemporalInvalidPlainYearMonth, "Invalid plain year month") \ + M(TemporalInvalidRelativeToStringUTCDesignatorWithoutBracketedTimeZone, "Invalid relativeTo string '{}': must not contain a UTC " \ + "designator without bracketed time zone") \ M(TemporalInvalidTime, "Invalid time") \ M(TemporalInvalidTimeString, "Invalid time string '{}'") \ M(TemporalInvalidTimeStringUTCDesignator, "Invalid time string '{}': must not contain a UTC designator") \ diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index d8d11e1103..4df74435e1 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -1609,12 +1609,18 @@ ThrowCompletionOr parse_temporal_month_day_string(VM& vm, Stri // 13.36 ParseTemporalRelativeToString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalrelativetostring ThrowCompletionOr parse_temporal_relative_to_string(VM& vm, String const& iso_string) { - // 1. If ParseText(StringToCodePoints(isoString), TemporalDateTimeString) is a List of errors, throw a RangeError exception. + // 1. Let parseResult be ParseText(StringToCodePoints(isoString), TemporalDateTimeString). auto parse_result = parse_iso8601(Production::TemporalDateTimeString, iso_string); + + // 2. If parseResult is a List of errors, throw a RangeError exception. if (!parse_result.has_value()) return vm.throw_completion(ErrorType::TemporalInvalidDateTimeString, iso_string); - // 2. Return ? ParseISODateTime(isoString). + // 3. If parseResult contains a UTCDesignator ParseNode but no TimeZoneBracketedAnnotation Parse Node, throw a RangeError exception. + if (parse_result->utc_designator.has_value() && !parse_result->time_zone_bracketed_annotation.has_value()) + return vm.throw_completion(ErrorType::TemporalInvalidRelativeToStringUTCDesignatorWithoutBracketedTimeZone, iso_string); + + // 4. Return ? ParseISODateTime(isoString). return parse_iso_date_time(vm, *parse_result); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp index ad66f79837..b8d1d0ffbe 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp @@ -830,6 +830,7 @@ bool ISO8601Parser::parse_time_zone_bracketed_annotation() return false; if (!m_state.lexer.consume_specific(']')) return false; + m_state.parse_result.time_zone_bracketed_annotation = transaction.parsed_string_view(); transaction.commit(); return true; } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h index 4e5bd70c2e..599368137a 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h @@ -24,6 +24,7 @@ struct ParseResult { Optional time_fraction; Optional calendar_name; Optional utc_designator; + Optional time_zone_bracketed_annotation; Optional time_zone_numeric_utc_offset; Optional time_zone_utc_offset_sign; Optional time_zone_utc_offset_hour; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js index 051701eed8..084e343c60 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js @@ -100,4 +100,14 @@ describe("errors", () => { Temporal.Duration.compare(duration, duration, { relativeTo: zonedDateTime }); }).toThrowWithMessage(TypeError, "null is not a function"); }); + + test("UTC designator only allowed with bracketed time zone", () => { + const duration = new Temporal.Duration(); + expect(() => { + Temporal.Duration.compare(duration, duration, { relativeTo: "2022-08-18T17:01Z" }); + }).toThrowWithMessage( + RangeError, + "Invalid relativeTo string '2022-08-18T17:01Z': must not contain a UTC designator without bracketed time zone" + ); + }); });