From 0e3def1d0bdd570f0132eeb1aa77885e5b12680f Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sat, 22 Jan 2022 21:52:55 +0000 Subject: [PATCH] LibJS: Don't parse/re-format offset in parse_temporal_time_zone_string() This is an editorial change in the Temporal spec. See: https://github.com/tc39/proposal-temporal/commit/6abe146 --- .../Runtime/Temporal/AbstractOperations.cpp | 97 ++++++------------- .../LibJS/Runtime/Temporal/ISO8601.cpp | 1 + .../LibJS/Runtime/Temporal/ISO8601.h | 1 + 3 files changed, 29 insertions(+), 70 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index 8978fad791..4e12280cff 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -1626,89 +1626,46 @@ ThrowCompletionOr parse_temporal_time_zone_string(GlobalObject { auto& vm = global_object.vm(); + auto optional_string_view_to_optional_string = [](Optional const& s) { + return s.has_value() ? String { *s } : Optional {}; + }; + // 1. Assert: Type(isoString) is String. - // 2. If isoString does not satisfy the syntax of a TemporalTimeZoneString (see 13.33), then + // 2. Let parseResult be ParseText(! StringToCodePoints(isoString), TemporalTimeZoneString). auto parse_result = parse_iso8601(Production::TemporalTimeZoneString, iso_string); + + // 3. If parseResult is a List of errors, then if (!parse_result.has_value()) { // a. Throw a RangeError exception. return vm.throw_completion(global_object, ErrorType::TemporalInvalidTimeZoneString, iso_string); } - // 3. Let z, sign, hours, minutes, seconds, fraction and name be the parts of isoString produced respectively by the UTCDesignator, TimeZoneUTCOffsetSign, TimeZoneUTCOffsetHour, TimeZoneUTCOffsetMinute, TimeZoneUTCOffsetSecond, TimeZoneUTCOffsetFractionalPart, and TimeZoneIANAName productions, or undefined if not present. - auto z_part = parse_result->utc_designator; - auto sign_part = parse_result->time_zone_utc_offset_sign; - auto hours_part = parse_result->time_zone_utc_offset_hour; - auto minutes_part = parse_result->time_zone_utc_offset_minute; - auto seconds_part = parse_result->time_zone_utc_offset_second; - auto fraction_part = parse_result->time_zone_utc_offset_fractional_part; - auto name_part = parse_result->time_zone_iana_name; + // 4. Let each of z, offsetString, and name be the source text matched by the respective UTCDesignator, TimeZoneNumericUTCOffset, and TimeZoneIANAName Parse Node enclosed by parseResult, or an empty sequence of code points if not present. + auto z = parse_result->utc_designator; + auto offset_string = parse_result->time_zone_numeric_utc_offset; + auto name = parse_result->time_zone_iana_name; - // 4. If z is not undefined, then - if (z_part.has_value()) { - // a. Return the Record { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: name }. - return TemporalTimeZone { .z = true, .offset_string = {}, .name = name_part.has_value() ? String { *name_part } : Optional {} }; - } - - Optional offset_string; - // 5. If hours is undefined, then - if (!hours_part.has_value()) { - // a. Let offsetString be undefined. - // NOTE: No-op. - } + // 5. If name is empty, then + // a. Set name to undefined. // 6. Else, - else { - // a. Assert: sign is not undefined. - VERIFY(sign_part.has_value()); + // a. Set name to ! CodePointsToString(name). + // NOTE: No-op. - // b. Set hours to ! ToIntegerOrInfinity(hours). - u8 hours = *hours_part->to_uint(); - - i8 sign; - // c. If sign is the code unit 0x002D (HYPHEN-MINUS) or the code unit 0x2212 (MINUS SIGN), then - if (sign_part->is_one_of("-", "\u2212")) { - // i. Set sign to −1. - sign = -1; - } - // d. Else, - else { - // i. Set sign to 1. - sign = 1; - } - - // e. Set minutes to ! ToIntegerOrInfinity(minutes). - u8 minutes = *minutes_part.value_or("0"sv).to_uint(); - - // f. Set seconds to ! ToIntegerOrInfinity(seconds). - u8 seconds = *seconds_part.value_or("0"sv).to_uint(); - - i32 nanoseconds; - // g. If fraction is not undefined, then - if (fraction_part.has_value()) { - // i. Set fraction to the string-concatenation of the previous value of fraction and the string "000000000". - auto fraction = String::formatted("{}000000000", *fraction_part); - // ii. Let nanoseconds be the String value equal to the substring of fraction from 1 to 10. - // iii. Set nanoseconds to ! ToIntegerOrInfinity(nanoseconds). - // FIXME: 1-10 is wrong and should be 0-9; the decimal separator is no longer present in the parsed string. - // See: https://github.com/tc39/proposal-temporal/pull/1999 - nanoseconds = *fraction.substring(0, 9).to_int(); - } - // h. Else, - else { - // i. Let nanoseconds be 0. - nanoseconds = 0; - } - - // i. Let offsetNanoseconds be sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds). - // NOTE: Decimal point in 10^9 is important, otherwise it's all integers and the result overflows! - auto offset_nanoseconds = sign * (((hours * 60 + minutes) * 60 + seconds) * 1000000000.0 + nanoseconds); - - // j. Let offsetString be ! FormatTimeZoneOffsetString(offsetNanoseconds). - offset_string = format_time_zone_offset_string(offset_nanoseconds); + // 7. If z is not empty, then + if (z.has_value()) { + // a. Return the Record { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: name }. + return TemporalTimeZone { .z = true, .offset_string = {}, .name = optional_string_view_to_optional_string(name) }; } - // 7. Return the Record { [[Z]]: false, [[OffsetString]]: offsetString, [[Name]]: name }. - return TemporalTimeZone { .z = false, .offset_string = offset_string, .name = name_part.has_value() ? String { *name_part } : Optional {} }; + // 8. If offsetString is empty, then + // a. Set offsetString to undefined. + // 9. Else, + // a. Set offsetString to ! CodePointsToString(offsetString). + // NOTE: No-op. + + // 10. Return the Record { [[Z]]: false, [[OffsetString]]: offsetString, [[Name]]: name }. + return TemporalTimeZone { .z = false, .offset_string = optional_string_view_to_optional_string(offset_string), .name = optional_string_view_to_optional_string(name) }; } // 13.44 ParseTemporalYearMonthString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalyearmonthstring diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp index fb83e64901..9af3ef742d 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp @@ -725,6 +725,7 @@ bool ISO8601Parser::parse_time_zone_numeric_utc_offset() if (parse_time_zone_utc_offset_second()) (void)parse_time_zone_utc_offset_fraction(); } + m_state.parse_result.time_zone_numeric_utc_offset = 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 4422b5a81e..5b631bac3d 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h @@ -31,6 +31,7 @@ struct ParseResult { Optional time_second_not_valid_month; Optional calendar_name; Optional utc_designator; + Optional time_zone_numeric_utc_offset; Optional time_zone_utc_offset_sign; Optional time_zone_utc_offset_hour; Optional time_zone_utc_offset_minute;