Просмотр исходного кода

LibJS: Implement parsing of TemporalZonedDateTimeString

Linus Groh 3 лет назад
Родитель
Сommit
98b876ad3f

+ 1 - 0
Userland/Libraries/LibJS/Runtime/ErrorTypes.h

@@ -230,6 +230,7 @@
     M(TemporalInvalidUnitRange, "Invalid unit range, {} is larger than {}")                                                             \
     M(TemporalInvalidYearMonthString, "Invalid year month string '{}'")                                                                 \
     M(TemporalInvalidZonedDateTimeOffset, "Invalid offset for the provided date and time in the current time zone")                     \
+    M(TemporalInvalidZonedDateTimeString, "Invalid zoned date time string '{}'")                                                        \
     M(TemporalMissingOptionsObject, "Required options object is missing or undefined")                                                  \
     M(TemporalMissingStartingPoint, "A starting point is required for balancing {}")                                                    \
     M(TemporalObjectMustHaveOneOf, "Object must have at least one of the following properties: {}")                                     \

+ 30 - 13
Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp

@@ -1192,14 +1192,19 @@ ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(GlobalObject& g
 // 13.36 ParseTemporalZonedDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalzoneddatetimestring
 ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_zoned_date_time_string(GlobalObject& global_object, String const& iso_string)
 {
+    auto& vm = global_object.vm();
+
     // 1. Assert: Type(isoString) is String.
 
     // 2. If isoString does not satisfy the syntax of a TemporalZonedDateTimeString (see 13.33), then
-    // a. Throw a RangeError exception.
-    // TODO
+    auto parse_result = parse_iso8601(Production::TemporalZonedDateTimeString, iso_string);
+    if (!parse_result.has_value()) {
+        // a. Throw a RangeError exception.
+        return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidZonedDateTimeString, iso_string);
+    }
 
     // 3. Let result be ! ParseISODateTime(isoString).
-    auto result = MUST(parse_iso_date_time(global_object, {}));
+    auto result = MUST(parse_iso_date_time(global_object, *parse_result));
 
     // 4. Let timeZoneResult be ? ParseTemporalTimeZoneString(isoString).
     auto time_zone_result = TRY(parse_temporal_time_zone_string(global_object, iso_string));
@@ -1335,18 +1340,30 @@ ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_relative_to_string(Globa
     Optional<String> offset;
     Optional<String> time_zone;
 
-    // TODO: 4. If isoString satisfies the syntax of a TemporalZonedDateTimeString (see 13.33), then
-    //          a. Let timeZoneResult be ! ParseTemporalTimeZoneString(isoString).
-    //          b. Let z be timeZoneResult.[[Z]].
-    //          c. Let offset be timeZoneResult.[[Offset]].
-    //          d. Let timeZone be timeZoneResult.[[Name]].
+    // 4. If isoString satisfies the syntax of a TemporalZonedDateTimeString (see 13.33), then
+    auto parse_result = parse_iso8601(Production::TemporalZonedDateTimeString, iso_string);
+    if (parse_result.has_value()) {
+        // a. Let timeZoneResult be ! ParseTemporalTimeZoneString(isoString).
+        // TODO: TRY() instead of MUST() as parse_temporal_time_zone_string() still throws more than it parses :^)
+        auto time_zone_result = TRY(parse_temporal_time_zone_string(global_object, iso_string));
+
+        // b. Let z be timeZoneResult.[[Z]].
+        z = time_zone_result.z;
+
+        // c. Let offset be timeZoneResult.[[Offset]].
+        offset = time_zone_result.offset;
 
-    // TODO: 5. Else,
-    // a. Let z be false.
-    z = false;
+        // d. Let timeZone be timeZoneResult.[[Name]].
+        time_zone = time_zone_result.name;
+    }
+    // 5. Else,
+    else {
+        // a. Let z be false.
+        z = false;
 
-    // b. Let offset be undefined. (NOTE: It already is)
-    // c. Let timeZone be undefined. (NOTE: It already is)
+        // b. Let offset be undefined.
+        // c. Let timeZone be undefined.
+    }
 
     // 6. Return the Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[Hour]]: result.[[Hour]], [[Minute]]: result.[[Minute]], [[Second]]: result.[[Second]], [[Millisecond]]: result.[[Millisecond]], [[Microsecond]]: result.[[Microsecond]], [[Nanosecond]]: result.[[Nanosecond]], [[Calendar]]: result.[[Calendar]], [[TimeZoneZ]]: z, [[TimeZoneOffset]]: offset, [[TimeZoneIANAName]]: timeZone }.
     return TemporalZonedDateTime { .date_time = move(result), .time_zone = { .z = z, .offset = move(offset), .name = move(time_zone) } };

+ 23 - 6
Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp

@@ -555,14 +555,31 @@ bool ISO8601Parser::parse_temporal_year_month_string()
         || parse_date_spec_year_month();
 }
 
+// https://tc39.es/proposal-temporal/#prod-TemporalZonedDateTimeString
+bool ISO8601Parser::parse_temporal_zoned_date_time_string()
+{
+    // TemporalZonedDateTimeString :
+    //     Date TimeSpecSeparator[opt] TimeZoneNameRequired Calendar[opt]
+    StateTransaction transaction { *this };
+    if (!parse_date())
+        return false;
+    (void)parse_time_spec_separator();
+    if (!parse_time_zone_name_required())
+        return false;
+    (void)parse_calendar();
+    transaction.commit();
+    return true;
+}
+
 }
 
-#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS                             \
-    __JS_ENUMERATE(TemporalDateString, parse_temporal_date_string)          \
-    __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string) \
-    __JS_ENUMERATE(TemporalMonthDayString, parse_temporal_month_day_string) \
-    __JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string)          \
-    __JS_ENUMERATE(TemporalYearMonthString, parse_temporal_year_month_string)
+#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS                               \
+    __JS_ENUMERATE(TemporalDateString, parse_temporal_date_string)            \
+    __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string)   \
+    __JS_ENUMERATE(TemporalMonthDayString, parse_temporal_month_day_string)   \
+    __JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string)            \
+    __JS_ENUMERATE(TemporalYearMonthString, parse_temporal_year_month_string) \
+    __JS_ENUMERATE(TemporalZonedDateTimeString, parse_temporal_zoned_date_time_string)
 
 Optional<ParseResult> parse_iso8601(Production production, StringView input)
 {

+ 2 - 0
Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h

@@ -31,6 +31,7 @@ enum class Production {
     TemporalMonthDayString,
     TemporalTimeString,
     TemporalYearMonthString,
+    TemporalZonedDateTimeString,
 };
 
 Optional<ParseResult> parse_iso8601(Production, StringView);
@@ -87,6 +88,7 @@ public:
     [[nodiscard]] bool parse_temporal_month_day_string();
     [[nodiscard]] bool parse_temporal_time_string();
     [[nodiscard]] bool parse_temporal_year_month_string();
+    [[nodiscard]] bool parse_temporal_zoned_date_time_string();
 
 private:
     struct State {

+ 6 - 0
Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.from.js

@@ -148,4 +148,10 @@ describe("errors", () => {
             });
         }).toThrowWithMessage(TypeError, "Required property day is missing or undefined");
     });
+
+    test("invalid zoned date time string", () => {
+        expect(() => {
+            Temporal.ZonedDateTime.from("foo");
+        }).toThrowWithMessage(RangeError, "Invalid zoned date time string 'foo'");
+    });
 });