From 3bd7f5b89ebd6d84ba51539a37827f9705d7236a Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Thu, 6 Jan 2022 19:00:48 +0100 Subject: [PATCH] LibJS: Fully parse the TimeZoneIANAName production Currently does nothing as we'll declare everything other than UTC as invalid, but it's a first step towards supporting named time zones :^) --- .../LibJS/Runtime/Temporal/ISO8601.cpp | 66 ++++++++++++++++--- .../LibJS/Runtime/Temporal/ISO8601.h | 6 +- .../ZonedDateTime.prototype.withTimeZone.js | 2 +- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp index 9367262dac..fb83e64901 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Linus Groh + * Copyright (c) 2021-2022, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ @@ -824,31 +824,81 @@ bool ISO8601Parser::parse_time_zone_utc_offset_name() return true; } -// https://tc39.es/proposal-temporal/#prod-TimeZoneIANAName -bool ISO8601Parser::parse_time_zone_iana_name() +// https://tc39.es/proposal-temporal/#prod-TZLeadingChar +bool ISO8601Parser::parse_tz_leading_char() { // TZLeadingChar : // Alpha // . // _ + if (m_state.lexer.next_is(is_ascii_alpha)) { + m_state.lexer.consume(); + return true; + } + return m_state.lexer.consume_specific('.') + || m_state.lexer.consume_specific('_'); +} + +// https://tc39.es/proposal-temporal/#prod-TZChar +bool ISO8601Parser::parse_tz_char() +{ // TZChar : // Alpha // . // - // _ + if (m_state.lexer.next_is(is_ascii_alpha)) { + m_state.lexer.consume(); + return true; + } + return m_state.lexer.consume_specific('.') + || m_state.lexer.consume_specific('-') + || m_state.lexer.consume_specific('_'); +} + +// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameComponent +bool ISO8601Parser::parse_time_zone_iana_component() +{ // TimeZoneIANANameComponent : // TZLeadingChar TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] but not one of . or .. + StateTransaction transaction { *this }; + if (!parse_tz_leading_char()) + return false; + for (size_t i = 0; i < 13; ++i) { + if (!parse_tz_char()) + break; + } + if (transaction.parsed_string_view().is_one_of("."sv, ".."sv)) + return false; + transaction.commit(); + return true; +} + +// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameTail +bool ISO8601Parser::parse_time_zone_iana_name_tail() +{ // TimeZoneIANANameTail : // TimeZoneIANANameComponent // TimeZoneIANANameComponent / TimeZoneIANANameTail + StateTransaction transaction { *this }; + if (!parse_time_zone_iana_component()) + return false; + while (m_state.lexer.next_is('/')) { + m_state.lexer.consume(); + if (!parse_time_zone_iana_component()) + return false; + } + transaction.commit(); + return true; +} + +// https://tc39.es/proposal-temporal/#prod-TimeZoneIANAName +bool ISO8601Parser::parse_time_zone_iana_name() +{ // TimeZoneIANAName : // TimeZoneIANANameTail StateTransaction transaction { *this }; - // TODO: Implement the full production. Currently, anything other than "UTC" would get rejected as unknown anyway. - auto success = (m_state.lexer.consume_specific('U') || m_state.lexer.consume_specific('u')) - && (m_state.lexer.consume_specific('T') || m_state.lexer.consume_specific('t')) - && (m_state.lexer.consume_specific('C') || m_state.lexer.consume_specific('c')); - if (!success) + if (!parse_time_zone_iana_name_tail()) return false; m_state.parse_result.time_zone_iana_name = transaction.parsed_string_view(); transaction.commit(); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h index a278f1f529..4422b5a81e 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Linus Groh + * Copyright (c) 2021-2022, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ @@ -131,6 +131,10 @@ public: [[nodiscard]] bool parse_time_zone_numeric_utc_offset_not_ambiguous(); [[nodiscard]] bool parse_time_zone_numeric_utc_offset_not_ambiguous_allowed_negative_hour(); [[nodiscard]] bool parse_time_zone_utc_offset_name(); + [[nodiscard]] bool parse_tz_leading_char(); + [[nodiscard]] bool parse_tz_char(); + [[nodiscard]] bool parse_time_zone_iana_component(); + [[nodiscard]] bool parse_time_zone_iana_name_tail(); [[nodiscard]] bool parse_time_zone_iana_name(); [[nodiscard]] bool parse_time_zone_bracketed_name(); [[nodiscard]] bool parse_time_zone_bracketed_annotation(); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withTimeZone.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withTimeZone.js index 7a391bcced..00bb9d7647 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withTimeZone.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withTimeZone.js @@ -36,6 +36,6 @@ describe("errors", () => { expect(() => { zonedDateTime.withTimeZone("UTCfoobar"); - }).toThrowWithMessage(RangeError, "Invalid time zone string 'UTCfoobar'"); + }).toThrowWithMessage(RangeError, "Invalid time zone name 'UTCfoobar'"); }); });