tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TemporalParser.cpp (87061B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "builtin/temporal/TemporalParser.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/Maybe.h"
     12 #include "mozilla/Result.h"
     13 #include "mozilla/Span.h"
     14 #include "mozilla/TextUtils.h"
     15 #include "mozilla/Try.h"
     16 
     17 #include <algorithm>
     18 #include <cstdlib>
     19 #include <initializer_list>
     20 #include <limits>
     21 #include <stdint.h>
     22 #include <string_view>
     23 #include <type_traits>
     24 #include <utility>
     25 
     26 #include "jsnum.h"
     27 #include "NamespaceImports.h"
     28 
     29 #include "builtin/temporal/Calendar.h"
     30 #include "builtin/temporal/Duration.h"
     31 #include "builtin/temporal/PlainDate.h"
     32 #include "builtin/temporal/PlainTime.h"
     33 #include "builtin/temporal/TemporalTypes.h"
     34 #include "builtin/temporal/TemporalUnit.h"
     35 #include "gc/Barrier.h"
     36 #include "gc/Tracer.h"
     37 #include "js/ErrorReport.h"
     38 #include "js/friend/ErrorMessages.h"
     39 #include "js/GCAPI.h"
     40 #include "js/RootingAPI.h"
     41 #include "js/TypeDecls.h"
     42 #include "util/Text.h"
     43 #include "vm/JSAtomState.h"
     44 #include "vm/JSContext.h"
     45 #include "vm/StringType.h"
     46 
     47 using namespace js;
     48 using namespace js::temporal;
     49 
     50 struct StringName final {
     51  // Start position and length of this name.
     52  size_t start = 0;
     53  size_t length = 0;
     54 
     55  bool present() const { return length > 0; }
     56 };
     57 
     58 static JSLinearString* ToString(JSContext* cx, JSString* string,
     59                                const StringName& name) {
     60  MOZ_ASSERT(name.present());
     61  return NewDependentString(cx, string, name.start, name.length);
     62 }
     63 
     64 template <typename CharT>
     65 bool EqualCharIgnoreCaseAscii(CharT c1, char c2) {
     66  if constexpr (sizeof(CharT) > sizeof(char)) {
     67    if (!mozilla::IsAscii(c1)) {
     68      return false;
     69    }
     70  }
     71 
     72  static constexpr auto toLower = 0x20;
     73  static_assert('a' - 'A' == toLower);
     74 
     75  // Convert both characters to lower case before the comparison.
     76  char c = c1;
     77  if (mozilla::IsAsciiUppercaseAlpha(c1)) {
     78    c = char(c + toLower);
     79  }
     80  char d = c2;
     81  if (mozilla::IsAsciiUppercaseAlpha(c2)) {
     82    d = char(d + toLower);
     83  }
     84  return c == d;
     85 }
     86 
     87 using CalendarName = StringName;
     88 using AnnotationKey = StringName;
     89 using AnnotationValue = StringName;
     90 using TimeZoneName = StringName;
     91 
     92 struct Annotation final {
     93  AnnotationKey key;
     94  AnnotationValue value;
     95  bool critical = false;
     96 };
     97 
     98 struct TimeSpec final {
     99  Time time;
    100 };
    101 
    102 struct TimeZoneUTCOffset final {
    103  // ±1 for time zones with an offset, otherwise 0.
    104  int32_t sign = 0;
    105 
    106  // An integer in the range [0, 23].
    107  int32_t hour = 0;
    108 
    109  // An integer in the range [0, 59].
    110  int32_t minute = 0;
    111 };
    112 
    113 struct DateTimeUTCOffset final {
    114  // ±1 for time zones with an offset, otherwise 0.
    115  int32_t sign = 0;
    116 
    117  // An integer in the range [0, 23].
    118  int32_t hour = 0;
    119 
    120  // An integer in the range [0, 59].
    121  int32_t minute = 0;
    122 
    123  // An integer in the range [0, 59].
    124  int32_t second = 0;
    125 
    126  // An integer in the range [0, 999'999].
    127  int32_t fractionalPart = 0;
    128 
    129  // Time zone with sub-minute precision.
    130  bool subMinutePrecision = false;
    131 
    132  // Convert to a TimeZoneUTCOffset.
    133  TimeZoneUTCOffset toTimeZoneUTCOffset() const {
    134    MOZ_ASSERT(!subMinutePrecision, "unexpected sub-minute precision");
    135    return {sign, hour, minute};
    136  }
    137 };
    138 
    139 /**
    140 * ParseDateTimeUTCOffset ( offsetString )
    141 */
    142 static auto ParseDateTimeUTCOffset(const DateTimeUTCOffset& offset) {
    143  constexpr int64_t nanoPerSec = 1'000'000'000;
    144 
    145  MOZ_ASSERT(offset.sign == -1 || offset.sign == +1);
    146  MOZ_ASSERT(0 <= offset.hour && offset.hour < 24);
    147  MOZ_ASSERT(0 <= offset.minute && offset.minute < 60);
    148  MOZ_ASSERT(0 <= offset.second && offset.second < 60);
    149  MOZ_ASSERT(0 <= offset.fractionalPart && offset.fractionalPart < nanoPerSec);
    150 
    151  // sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds).
    152  int64_t seconds = (offset.hour * 60 + offset.minute) * 60 + offset.second;
    153  int64_t nanos = (seconds * nanoPerSec) + offset.fractionalPart;
    154  int64_t result = offset.sign * nanos;
    155 
    156  MOZ_ASSERT(std::abs(result) < ToNanoseconds(TemporalUnit::Day),
    157             "time zone offset is less than 24:00 hours");
    158 
    159  return OffsetTimeZone{result, offset.subMinutePrecision};
    160 }
    161 
    162 static int32_t ParseTimeZoneOffset(const TimeZoneUTCOffset& offset) {
    163  MOZ_ASSERT(offset.sign == -1 || offset.sign == +1);
    164  MOZ_ASSERT(0 <= offset.hour && offset.hour < 24);
    165  MOZ_ASSERT(0 <= offset.minute && offset.minute < 60);
    166 
    167  // sign × (hour × 60 + minute).
    168  int32_t result = offset.sign * (offset.hour * 60 + offset.minute);
    169 
    170  MOZ_ASSERT(std::abs(result) < UnitsPerDay(TemporalUnit::Minute),
    171             "time zone offset is less than 24:00 hours");
    172 
    173  return result;
    174 }
    175 
    176 /**
    177 * Struct to hold time zone annotations.
    178 */
    179 struct TimeZoneAnnotation final {
    180  // Time zone offset.
    181  TimeZoneUTCOffset offset;
    182 
    183  // Time zone name.
    184  TimeZoneName name;
    185 
    186  /**
    187   * Returns true iff the time zone has an offset part, e.g. "+01:00".
    188   */
    189  bool hasOffset() const { return offset.sign != 0; }
    190 
    191  /**
    192   * Returns true iff the time zone has an IANA name, e.g. "Asia/Tokyo".
    193   */
    194  bool hasName() const { return name.present(); }
    195 };
    196 
    197 /**
    198 * Struct to hold any time zone parts of a parsed string.
    199 */
    200 struct TimeZoneString final {
    201  // Date-time UTC offset.
    202  DateTimeUTCOffset offset;
    203 
    204  // Time zone annotation;
    205  TimeZoneAnnotation annotation;
    206 
    207  // UTC time zone.
    208  bool utc = false;
    209 
    210  static auto from(DateTimeUTCOffset offset) {
    211    TimeZoneString timeZone{};
    212    timeZone.offset = offset;
    213    return timeZone;
    214  }
    215 
    216  static auto from(TimeZoneUTCOffset offset) {
    217    TimeZoneString timeZone{};
    218    timeZone.annotation.offset = offset;
    219    return timeZone;
    220  }
    221 
    222  static auto from(TimeZoneName name) {
    223    TimeZoneString timeZone{};
    224    timeZone.annotation.name = name;
    225    return timeZone;
    226  }
    227 
    228  static auto UTC() {
    229    TimeZoneString timeZone{};
    230    timeZone.utc = true;
    231    return timeZone;
    232  }
    233 
    234  /**
    235   * Returns true iff the time zone has an offset part, e.g. "+01:00".
    236   */
    237  bool hasOffset() const { return offset.sign != 0; }
    238 
    239  /**
    240   * Returns true iff the time zone has an annotation.
    241   */
    242  bool hasAnnotation() const {
    243    return annotation.hasName() || annotation.hasOffset();
    244  }
    245 
    246  /**
    247   * Returns true iff the time zone uses the "Z" abbrevation to denote UTC time.
    248   */
    249  bool isUTC() const { return utc; }
    250 };
    251 
    252 /**
    253 * Struct to hold the parsed date, time, time zone, and calendar components.
    254 */
    255 struct ZonedDateTimeString final {
    256  ISODate date;
    257  Time time;
    258  TimeZoneString timeZone;
    259  CalendarName calendar;
    260  bool startOfDay;
    261 };
    262 
    263 template <typename CharT>
    264 static bool IsISO8601Calendar(mozilla::Span<const CharT> calendar) {
    265  static constexpr std::string_view iso8601 = "iso8601";
    266 
    267  if (calendar.size() != iso8601.length()) {
    268    return false;
    269  }
    270 
    271  for (size_t i = 0; i < iso8601.length(); i++) {
    272    if (!EqualCharIgnoreCaseAscii(calendar[i], iso8601[i])) {
    273      return false;
    274    }
    275  }
    276  return true;
    277 }
    278 
    279 static constexpr int32_t AbsentYear = INT32_MAX;
    280 
    281 /**
    282 * ParseISODateTime ( isoString )
    283 */
    284 static bool ParseISODateTime(JSContext* cx, const ZonedDateTimeString& parsed,
    285                             ISODateTime* result) {
    286  // Steps 1-7, 9, 11-16 (Not applicable here).
    287 
    288  ISODateTime dateTime = {parsed.date, parsed.time};
    289 
    290  // NOTE: ToIntegerOrInfinity("") is 0.
    291  if (dateTime.date.year == AbsentYear) {
    292    dateTime.date.year = 0;
    293  }
    294 
    295  // Step 8.
    296  if (dateTime.date.month == 0) {
    297    dateTime.date.month = 1;
    298  }
    299 
    300  // Step 10.
    301  if (dateTime.date.day == 0) {
    302    dateTime.date.day = 1;
    303  }
    304 
    305  // Step 17.b.
    306  if (dateTime.time.second == 60) {
    307    dateTime.time.second = 59;
    308  }
    309 
    310  // ParseISODateTime, steps 18-19 (Not applicable in our implementation).
    311 
    312  // Perform early error checks now that absent |day| and |month| values were
    313  // handled:
    314  // `IsValidDate(DateSpec)` and `IsValidMonthDay(DateSpecMonthDay)` validate
    315  // that |day| doesn't exceed the number of days in |month|. This check can be
    316  // implemented by calling `ThrowIfInvalidISODate`.
    317  //
    318  // All other values are already in-bounds.
    319  MOZ_ASSERT(std::abs(dateTime.date.year) <= 999'999);
    320  MOZ_ASSERT(1 <= dateTime.date.month && dateTime.date.month <= 12);
    321  MOZ_ASSERT(1 <= dateTime.date.day && dateTime.date.day <= 31);
    322 
    323  if (!ThrowIfInvalidISODate(cx, dateTime.date)) {
    324    return false;
    325  }
    326 
    327  // Step 20.
    328  MOZ_ASSERT(IsValidISODate(dateTime.date));
    329 
    330  // Step 21.
    331  MOZ_ASSERT(IsValidTime(dateTime.time));
    332 
    333  // Steps 22-28. (Handled in caller.)
    334 
    335  *result = dateTime;
    336  return true;
    337 }
    338 
    339 static bool ParseTimeZoneAnnotation(JSContext* cx,
    340                                    const TimeZoneAnnotation& annotation,
    341                                    JSLinearString* linear,
    342                                    MutableHandle<ParsedTimeZone> result) {
    343  MOZ_ASSERT(annotation.hasOffset() || annotation.hasName());
    344 
    345  if (annotation.hasOffset()) {
    346    int32_t offset = ParseTimeZoneOffset(annotation.offset);
    347    result.set(ParsedTimeZone::fromOffset(offset));
    348    return true;
    349  }
    350 
    351  auto* str = ToString(cx, linear, annotation.name);
    352  if (!str) {
    353    return false;
    354  }
    355  result.set(ParsedTimeZone::fromName(str));
    356  return true;
    357 }
    358 
    359 /**
    360 * Struct for the parsed duration components.
    361 */
    362 struct TemporalDurationString final {
    363  // A non-negative integer or +Infinity.
    364  double years = 0;
    365 
    366  // A non-negative integer or +Infinity.
    367  double months = 0;
    368 
    369  // A non-negative integer or +Infinity.
    370  double weeks = 0;
    371 
    372  // A non-negative integer or +Infinity.
    373  double days = 0;
    374 
    375  // A non-negative integer or +Infinity.
    376  double hours = 0;
    377 
    378  // A non-negative integer or +Infinity.
    379  double minutes = 0;
    380 
    381  // A non-negative integer or +Infinity.
    382  double seconds = 0;
    383 
    384  // An integer in the range [0, 999'999].
    385  int32_t hoursFraction = 0;
    386 
    387  // An integer in the range [0, 999'999].
    388  int32_t minutesFraction = 0;
    389 
    390  // An integer in the range [0, 999'999].
    391  int32_t secondsFraction = 0;
    392 
    393  // ±1 when an offset is present, otherwise 0.
    394  int32_t sign = 0;
    395 };
    396 
    397 class ParserError final {
    398  JSErrNum error_ = JSMSG_NOT_AN_ERROR;
    399 
    400 public:
    401  constexpr ParserError() = default;
    402 
    403  constexpr MOZ_IMPLICIT ParserError(JSErrNum error) : error_(error) {}
    404 
    405  constexpr JSErrNum error() const { return error_; }
    406 
    407  constexpr operator JSErrNum() const { return error(); }
    408 };
    409 
    410 namespace mozilla::detail {
    411 // Zero is used for tagging, so it mustn't be an error.
    412 static_assert(static_cast<JSErrNum>(0) == JSMSG_NOT_AN_ERROR);
    413 
    414 // Ensure efficient packing of the error type.
    415 template <>
    416 struct UnusedZero<::ParserError> {
    417 private:
    418  using Error = ::ParserError;
    419  using ErrorKind = JSErrNum;
    420 
    421 public:
    422  using StorageType = std::underlying_type_t<ErrorKind>;
    423 
    424  static constexpr bool value = true;
    425  static constexpr StorageType nullValue = 0;
    426 
    427  static constexpr Error Inspect(const StorageType& aValue) {
    428    return Error(static_cast<ErrorKind>(aValue));
    429  }
    430  static constexpr Error Unwrap(StorageType aValue) {
    431    return Error(static_cast<ErrorKind>(aValue));
    432  }
    433  static constexpr StorageType Store(Error aValue) {
    434    return static_cast<StorageType>(aValue.error());
    435  }
    436 };
    437 }  // namespace mozilla::detail
    438 
    439 static_assert(mozilla::Result<ZonedDateTimeString, ParserError>::Strategy !=
    440              mozilla::detail::PackingStrategy::Variant);
    441 
    442 /**
    443 * Track the error and reader index of the last largest successful parse.
    444 */
    445 class LikelyError final {
    446  size_t index_ = 0;
    447  ParserError error_{};
    448 
    449 public:
    450  template <typename V>
    451  void update(const mozilla::Result<V, ParserError>& result, size_t index) {
    452    MOZ_ASSERT(result.isErr());
    453 
    454    if (index >= index_) {
    455      index_ = index;
    456      error_ = result.inspectErr();
    457    }
    458  }
    459 
    460  size_t index() const { return index_; }
    461 
    462  auto propagate() const { return mozilla::Err(error_); }
    463 };
    464 
    465 template <typename CharT>
    466 class StringReader final {
    467  mozilla::Span<const CharT> string_;
    468 
    469  // Current position in the string.
    470  size_t index_ = 0;
    471 
    472 public:
    473  explicit StringReader(mozilla::Span<const CharT> string) : string_(string) {}
    474 
    475  /**
    476   * Returns the input string.
    477   */
    478  mozilla::Span<const CharT> string() const { return string_; }
    479 
    480  /**
    481   * Returns a substring of the input string.
    482   */
    483  mozilla::Span<const CharT> substring(const StringName& name) const {
    484    MOZ_ASSERT(name.present());
    485    return string_.Subspan(name.start, name.length);
    486  }
    487 
    488  /**
    489   * Returns the current parse position.
    490   */
    491  size_t index() const { return index_; }
    492 
    493  /**
    494   * Returns the length of the input string-
    495   */
    496  size_t length() const { return string_.size(); }
    497 
    498  /**
    499   * Returns true iff the whole string has been parsed.
    500   */
    501  bool atEnd() const { return index() == length(); }
    502 
    503  /**
    504   * Reset the parser to a previous parse position.
    505   */
    506  void reset(size_t index = 0) {
    507    MOZ_ASSERT(index <= length());
    508    index_ = index;
    509  }
    510 
    511  /**
    512   * Returns true if at least `amount` characters can be read from the current
    513   * parse position.
    514   */
    515  bool hasMore(size_t amount) const { return index() + amount <= length(); }
    516 
    517  /**
    518   * Advances the parse position by `amount` characters.
    519   */
    520  void advance(size_t amount) {
    521    MOZ_ASSERT(hasMore(amount));
    522    index_ += amount;
    523  }
    524 
    525  /**
    526   * Returns the character at the current parse position.
    527   */
    528  CharT current() const { return string()[index()]; }
    529 
    530  /**
    531   * Returns the character at the next parse position.
    532   */
    533  CharT next() const { return string()[index() + 1]; }
    534 
    535  /**
    536   * Returns the character at position `index`.
    537   */
    538  CharT at(size_t index) const { return string()[index]; }
    539 };
    540 
    541 template <typename CharT>
    542 class TemporalParser final {
    543  StringReader<CharT> reader_;
    544 
    545  /**
    546   * Read an unlimited amount of decimal digits, returning `Nothing` if no
    547   * digits were read.
    548   */
    549  mozilla::Maybe<double> digits(JSContext* cx);
    550 
    551  /**
    552   * Read exactly `length` digits, returning `Nothing` on failure.
    553   */
    554  mozilla::Maybe<int32_t> digits(size_t length) {
    555    MOZ_ASSERT(length > 0, "can't read zero digits");
    556    MOZ_ASSERT(length <= std::numeric_limits<int32_t>::digits10,
    557               "can't read more than digits10 digits without overflow");
    558 
    559    if (!reader_.hasMore(length)) {
    560      return mozilla::Nothing();
    561    }
    562    int32_t num = 0;
    563    size_t index = reader_.index();
    564    for (size_t i = 0; i < length; i++) {
    565      auto ch = reader_.at(index + i);
    566      if (!mozilla::IsAsciiDigit(ch)) {
    567        return mozilla::Nothing();
    568      }
    569      num = num * 10 + AsciiDigitToNumber(ch);
    570    }
    571    reader_.advance(length);
    572    return mozilla::Some(num);
    573  }
    574 
    575  // TemporalDecimalFraction :::
    576  //   TemporalDecimalSeparator DecimalDigit{1,9}
    577  mozilla::Maybe<int32_t> fraction() {
    578    if (!reader_.hasMore(2)) {
    579      return mozilla::Nothing();
    580    }
    581    if (!hasDecimalSeparator() || !mozilla::IsAsciiDigit(reader_.next())) {
    582      return mozilla::Nothing();
    583    }
    584 
    585    // Consume the decimal separator.
    586    MOZ_ALWAYS_TRUE(decimalSeparator());
    587 
    588    // Maximal nine fractional digits are supported.
    589    constexpr size_t maxFractions = 9;
    590 
    591    // Read up to |maxFractions| digits.
    592    int32_t num = 0;
    593    size_t index = reader_.index();
    594    size_t i = 0;
    595    for (; i < std::min(reader_.length() - index, maxFractions); i++) {
    596      CharT ch = reader_.at(index + i);
    597      if (!mozilla::IsAsciiDigit(ch)) {
    598        break;
    599      }
    600      num = num * 10 + AsciiDigitToNumber(ch);
    601    }
    602 
    603    // Skip past the read digits.
    604    reader_.advance(i);
    605 
    606    // Normalize the fraction to |maxFractions| digits.
    607    for (; i < maxFractions; i++) {
    608      num *= 10;
    609    }
    610    return mozilla::Some(num);
    611  }
    612 
    613  /**
    614   * Returns true iff the current character is `ch`.
    615   */
    616  bool hasCharacter(CharT ch) const {
    617    return reader_.hasMore(1) && reader_.current() == ch;
    618  }
    619 
    620  /**
    621   * Consumes the current character if it's equal to `ch` and then returns
    622   * `true`. Otherwise returns `false`.
    623   */
    624  bool character(CharT ch) {
    625    if (!hasCharacter(ch)) {
    626      return false;
    627    }
    628    reader_.advance(1);
    629    return true;
    630  }
    631 
    632  /**
    633   * Consumes the next characters if they're equal to `str` and then returns
    634   * `true`. Otherwise returns `false`.
    635   */
    636  template <size_t N>
    637  bool string(const char (&str)[N]) {
    638    static_assert(N > 2, "use character() for one element strings");
    639 
    640    if (!reader_.hasMore(N - 1)) {
    641      return false;
    642    }
    643    size_t index = reader_.index();
    644    for (size_t i = 0; i < N - 1; i++) {
    645      if (reader_.at(index + i) != str[i]) {
    646        return false;
    647      }
    648    }
    649    reader_.advance(N - 1);
    650    return true;
    651  }
    652 
    653  /**
    654   * Returns true if the next two characters are ASCII alphabetic characters.
    655   */
    656  bool hasTwoAsciiAlpha() {
    657    if (!reader_.hasMore(2)) {
    658      return false;
    659    }
    660    size_t index = reader_.index();
    661    return mozilla::IsAsciiAlpha(reader_.at(index)) &&
    662           mozilla::IsAsciiAlpha(reader_.at(index + 1));
    663  }
    664 
    665  /**
    666   * Returns true iff the current character is one of `chars`.
    667   */
    668  bool hasOneOf(std::initializer_list<char> chars) const {
    669    if (!reader_.hasMore(1)) {
    670      return false;
    671    }
    672    auto ch = reader_.current();
    673    return std::find(chars.begin(), chars.end(), ch) != chars.end();
    674  }
    675 
    676  /**
    677   * Consumes the current character if it's in `chars` and then returns `true`.
    678   * Otherwise returns `false`.
    679   */
    680  bool oneOf(std::initializer_list<char> chars) {
    681    if (!hasOneOf(chars)) {
    682      return false;
    683    }
    684    reader_.advance(1);
    685    return true;
    686  }
    687 
    688  /**
    689   * Consumes the current character if it matches the predicate and then returns
    690   * `true`. Otherwise returns `false`.
    691   */
    692  template <typename Predicate>
    693  bool matches(Predicate&& predicate) {
    694    if (!reader_.hasMore(1)) {
    695      return false;
    696    }
    697 
    698    CharT ch = reader_.current();
    699    if (!predicate(ch)) {
    700      return false;
    701    }
    702 
    703    reader_.advance(1);
    704    return true;
    705  }
    706 
    707  // ASCIISign ::: one of
    708  //   + -
    709  bool hasSign() const { return hasOneOf({'+', '-'}); }
    710 
    711  /**
    712   * Consumes the current character, which must be a sign character, and returns
    713   * its numeric value.
    714   */
    715  int32_t sign() {
    716    MOZ_ASSERT(hasSign());
    717    int32_t plus = hasCharacter('+');
    718    reader_.advance(1);
    719    return plus ? 1 : -1;
    720  }
    721 
    722  // DateSeparator[Extended] :::
    723  //   [+Extended] -
    724  //   [~Extended] [empty]
    725  bool dateSeparator() { return character('-'); }
    726 
    727  // TimeSeparator[Extended] :::
    728  //   [+Extended] :
    729  //   [~Extended] [empty]
    730  bool hasTimeSeparator() const { return hasCharacter(':'); }
    731 
    732  bool timeSeparator() { return character(':'); }
    733 
    734  // TemporalDecimalSeparator ::: one of
    735  //   . ,
    736  bool hasDecimalSeparator() const { return hasOneOf({'.', ','}); }
    737 
    738  bool decimalSeparator() { return oneOf({'.', ','}); }
    739 
    740  // DaysDesignator ::: one of
    741  //   D d
    742  bool daysDesignator() { return oneOf({'D', 'd'}); }
    743 
    744  // HoursDesignator ::: one of
    745  //   H h
    746  bool hoursDesignator() { return oneOf({'H', 'h'}); }
    747 
    748  // MinutesDesignator ::: one of
    749  //   M m
    750  bool minutesDesignator() { return oneOf({'M', 'm'}); }
    751 
    752  // MonthsDesignator ::: one of
    753  //   M m
    754  bool monthsDesignator() { return oneOf({'M', 'm'}); }
    755 
    756  // DurationDesignator ::: one of
    757  //   P p
    758  bool durationDesignator() { return oneOf({'P', 'p'}); }
    759 
    760  // SecondsDesignator ::: one of
    761  //   S s
    762  bool secondsDesignator() { return oneOf({'S', 's'}); }
    763 
    764  // DateTimeSeparator :::
    765  //   <SP>
    766  //   T
    767  //   t
    768  bool dateTimeSeparator() { return oneOf({' ', 'T', 't'}); }
    769 
    770  // TimeDesignator ::: one of
    771  //   T t
    772  bool hasTimeDesignator() const { return hasOneOf({'T', 't'}); }
    773 
    774  bool timeDesignator() { return oneOf({'T', 't'}); }
    775 
    776  // WeeksDesignator ::: one of
    777  //   W w
    778  bool weeksDesignator() { return oneOf({'W', 'w'}); }
    779 
    780  // YearsDesignator ::: one of
    781  //   Y y
    782  bool yearsDesignator() { return oneOf({'Y', 'y'}); }
    783 
    784  // UTCDesignator ::: one of
    785  //   Z z
    786  bool utcDesignator() { return oneOf({'Z', 'z'}); }
    787 
    788  // TZLeadingChar :::
    789  //   Alpha
    790  //   .
    791  //   _
    792  bool tzLeadingChar() {
    793    return matches([](auto ch) {
    794      return mozilla::IsAsciiAlpha(ch) || ch == '.' || ch == '_';
    795    });
    796  }
    797 
    798  // TZChar :::
    799  //   TZLeadingChar
    800  //   DecimalDigit
    801  //   -
    802  //   +
    803  bool tzChar() {
    804    return matches([](auto ch) {
    805      return mozilla::IsAsciiAlphanumeric(ch) || ch == '.' || ch == '_' ||
    806             ch == '-' || ch == '+';
    807    });
    808  }
    809 
    810  // AnnotationCriticalFlag :::
    811  //   !
    812  bool annotationCriticalFlag() { return character('!'); }
    813 
    814  // AKeyLeadingChar :::
    815  //   LowercaseAlpha
    816  //   _
    817  bool aKeyLeadingChar() {
    818    return matches([](auto ch) {
    819      return mozilla::IsAsciiLowercaseAlpha(ch) || ch == '_';
    820    });
    821  }
    822 
    823  // AKeyChar :::
    824  //   AKeyLeadingChar
    825  //   DecimalDigit
    826  //   -
    827  bool aKeyChar() {
    828    return matches([](auto ch) {
    829      return mozilla::IsAsciiLowercaseAlpha(ch) || mozilla::IsAsciiDigit(ch) ||
    830             ch == '-' || ch == '_';
    831    });
    832  }
    833 
    834  // AnnotationValueComponent :::
    835  //   Alpha AnnotationValueComponent?
    836  //   DecimalDigit AnnotationValueComponent?
    837  bool annotationValueComponent() {
    838    size_t index = reader_.index();
    839    size_t i = 0;
    840    for (; index + i < reader_.length(); i++) {
    841      auto ch = reader_.at(index + i);
    842      if (!mozilla::IsAsciiAlphanumeric(ch)) {
    843        break;
    844      }
    845    }
    846    if (i == 0) {
    847      return false;
    848    }
    849    reader_.advance(i);
    850    return true;
    851  }
    852 
    853  template <typename T>
    854  static constexpr bool inBounds(const T& x, const T& min, const T& max) {
    855    return min <= x && x <= max;
    856  }
    857 
    858  static auto err(JSErrNum error) {
    859    // Explicitly create |ParserError| when JSErrNum is auto-convertible to the
    860    // success type.
    861    return mozilla::Err(ParserError{error});
    862  }
    863 
    864  mozilla::Result<int32_t, ParserError> dateYear();
    865  mozilla::Result<int32_t, ParserError> dateMonth();
    866  mozilla::Result<int32_t, ParserError> dateDay();
    867  mozilla::Result<int32_t, ParserError> hour();
    868  mozilla::Result<mozilla::Maybe<int32_t>, ParserError> minute(bool required);
    869  mozilla::Result<mozilla::Maybe<int32_t>, ParserError> second(bool required);
    870  mozilla::Result<mozilla::Maybe<int32_t>, ParserError> timeSecond(
    871      bool required);
    872 
    873  mozilla::Result<ISODate, ParserError> date();
    874 
    875  mozilla::Result<Time, ParserError> time();
    876 
    877  mozilla::Result<ZonedDateTimeString, ParserError> dateTime(bool allowZ);
    878 
    879  mozilla::Result<ISODate, ParserError> dateSpecYearMonth();
    880 
    881  mozilla::Result<ISODate, ParserError> dateSpecMonthDay();
    882 
    883  // Return true when |Annotation| can start at the current position.
    884  bool hasAnnotationStart() const { return hasCharacter('['); }
    885 
    886  // Return true when |TimeZoneAnnotation| can start at the current position.
    887  bool hasTimeZoneAnnotationStart() const {
    888    if (!hasCharacter('[')) {
    889      return false;
    890    }
    891 
    892    // Ensure no '=' is found before the closing ']', otherwise the opening '['
    893    // may actually start an |Annotation| instead of a |TimeZoneAnnotation|.
    894    for (size_t i = reader_.index() + 1; i < reader_.length(); i++) {
    895      CharT ch = reader_.at(i);
    896      if (ch == '=') {
    897        return false;
    898      }
    899      if (ch == ']') {
    900        break;
    901      }
    902    }
    903    return true;
    904  }
    905 
    906  // Return true when |DateTimeUTCOffset| can start at the current position.
    907  bool hasDateTimeUTCOffsetStart() { return hasOneOf({'Z', 'z', '+', '-'}); }
    908 
    909  mozilla::Result<TimeZoneString, ParserError> dateTimeUTCOffset(bool allowZ);
    910 
    911  mozilla::Result<DateTimeUTCOffset, ParserError> utcOffsetSubMinutePrecision();
    912 
    913  mozilla::Result<TimeZoneUTCOffset, ParserError> timeZoneUTCOffsetName();
    914 
    915  mozilla::Result<TimeZoneAnnotation, ParserError> timeZoneIdentifier();
    916 
    917  mozilla::Result<TimeZoneAnnotation, ParserError> timeZoneAnnotation();
    918 
    919  mozilla::Result<TimeZoneName, ParserError> timeZoneIANAName();
    920 
    921  mozilla::Result<AnnotationKey, ParserError> annotationKey();
    922  mozilla::Result<AnnotationValue, ParserError> annotationValue();
    923  mozilla::Result<Annotation, ParserError> annotation();
    924  mozilla::Result<CalendarName, ParserError> annotations();
    925 
    926  mozilla::Result<ZonedDateTimeString, ParserError> annotatedTime();
    927 
    928  mozilla::Result<ZonedDateTimeString, ParserError> annotatedDateTime();
    929 
    930  mozilla::Result<ZonedDateTimeString, ParserError>
    931  annotatedDateTimeTimeRequired();
    932 
    933  mozilla::Result<ZonedDateTimeString, ParserError> annotatedYearMonth();
    934 
    935  mozilla::Result<ZonedDateTimeString, ParserError> annotatedMonthDay();
    936 
    937  mozilla::Result<double, ParserError> durationDigits(JSContext* cx);
    938 
    939  template <typename T>
    940  mozilla::Result<T, ParserError> parse(
    941      mozilla::Result<T, ParserError>&& result) const;
    942 
    943  template <typename T>
    944  mozilla::Result<T, ParserError> complete(const T& value) const;
    945 
    946  mozilla::Result<mozilla::Ok, ParserError> nonempty() const;
    947 
    948 public:
    949  explicit TemporalParser(mozilla::Span<const CharT> str) : reader_(str) {}
    950 
    951  mozilla::Result<ZonedDateTimeString, ParserError>
    952  parseTemporalInstantString();
    953 
    954  mozilla::Result<ZonedDateTimeString, ParserError>
    955  parseTemporalTimeZoneString();
    956 
    957  mozilla::Result<TimeZoneAnnotation, ParserError> parseTimeZoneIdentifier();
    958 
    959  mozilla::Result<DateTimeUTCOffset, ParserError> parseDateTimeUTCOffset();
    960 
    961  mozilla::Result<TemporalDurationString, ParserError>
    962  parseTemporalDurationString(JSContext* cx);
    963 
    964  mozilla::Result<ZonedDateTimeString, ParserError>
    965  parseTemporalCalendarString();
    966 
    967  mozilla::Result<ZonedDateTimeString, ParserError> parseTemporalTimeString();
    968 
    969  mozilla::Result<ZonedDateTimeString, ParserError>
    970  parseTemporalMonthDayString();
    971 
    972  mozilla::Result<ZonedDateTimeString, ParserError>
    973  parseTemporalYearMonthString();
    974 
    975  mozilla::Result<ZonedDateTimeString, ParserError>
    976  parseTemporalDateTimeString();
    977 
    978  mozilla::Result<ZonedDateTimeString, ParserError>
    979  parseTemporalZonedDateTimeString();
    980 
    981  mozilla::Result<ZonedDateTimeString, ParserError>
    982  parseTemporalRelativeToString();
    983 };
    984 
    985 template <typename CharT>
    986 template <typename T>
    987 mozilla::Result<T, ParserError> TemporalParser<CharT>::parse(
    988    mozilla::Result<T, ParserError>&& result) const {
    989  if (result.isOk() && !reader_.atEnd()) {
    990    return mozilla::Err(JSMSG_TEMPORAL_PARSER_UNEXPECTED_CHARACTERS_AT_END);
    991  }
    992  return std::move(result);
    993 }
    994 
    995 template <typename CharT>
    996 template <typename T>
    997 mozilla::Result<T, ParserError> TemporalParser<CharT>::complete(
    998    const T& result) const {
    999  if (!reader_.atEnd()) {
   1000    return mozilla::Err(JSMSG_TEMPORAL_PARSER_UNEXPECTED_CHARACTERS_AT_END);
   1001  }
   1002  return result;
   1003 }
   1004 
   1005 template <typename CharT>
   1006 mozilla::Result<mozilla::Ok, ParserError> TemporalParser<CharT>::nonempty()
   1007    const {
   1008  if (reader_.length() == 0) {
   1009    return mozilla::Err(JSMSG_TEMPORAL_PARSER_EMPTY_STRING);
   1010  }
   1011  return mozilla::Ok{};
   1012 }
   1013 
   1014 template <typename CharT>
   1015 mozilla::Result<int32_t, ParserError> TemporalParser<CharT>::dateYear() {
   1016  // DateYear :::
   1017  //  DecimalDigit{4}
   1018  //  ASCIISign DecimalDigit{6}
   1019 
   1020  if (auto year = digits(4)) {
   1021    return year.value();
   1022  }
   1023  if (hasSign()) {
   1024    int32_t yearSign = sign();
   1025    if (auto year = digits(6)) {
   1026      int32_t result = yearSign * year.value();
   1027      if (yearSign < 0 && result == 0) {
   1028        return err(JSMSG_TEMPORAL_PARSER_NEGATIVE_ZERO_YEAR);
   1029      }
   1030      return result;
   1031    }
   1032    return err(JSMSG_TEMPORAL_PARSER_MISSING_EXTENDED_YEAR);
   1033  }
   1034  return err(JSMSG_TEMPORAL_PARSER_MISSING_YEAR);
   1035 }
   1036 
   1037 template <typename CharT>
   1038 mozilla::Result<int32_t, ParserError> TemporalParser<CharT>::dateMonth() {
   1039  // DateMonth :::
   1040  //   0 NonzeroDigit
   1041  //   10
   1042  //   11
   1043  //   12
   1044  if (auto month = digits(2)) {
   1045    int32_t result = month.value();
   1046    if (!inBounds(result, 1, 12)) {
   1047      return err(JSMSG_TEMPORAL_PARSER_INVALID_MONTH);
   1048    }
   1049    return result;
   1050  }
   1051  return err(JSMSG_TEMPORAL_PARSER_MISSING_MONTH);
   1052 }
   1053 
   1054 template <typename CharT>
   1055 mozilla::Result<int32_t, ParserError> TemporalParser<CharT>::dateDay() {
   1056  // DateDay :::
   1057  //   0 NonzeroDigit
   1058  //   1 DecimalDigit
   1059  //   2 DecimalDigit
   1060  //   30
   1061  //   31
   1062  if (auto day = digits(2)) {
   1063    int32_t result = day.value();
   1064    if (!inBounds(result, 1, 31)) {
   1065      return err(JSMSG_TEMPORAL_PARSER_INVALID_DAY);
   1066    }
   1067    return result;
   1068  }
   1069  return err(JSMSG_TEMPORAL_PARSER_MISSING_DAY);
   1070 }
   1071 
   1072 template <typename CharT>
   1073 mozilla::Result<int32_t, ParserError> TemporalParser<CharT>::hour() {
   1074  // Hour :::
   1075  //   0 DecimalDigit
   1076  //   1 DecimalDigit
   1077  //   20
   1078  //   21
   1079  //   22
   1080  //   23
   1081  if (auto hour = digits(2)) {
   1082    int32_t result = hour.value();
   1083    if (!inBounds(result, 0, 23)) {
   1084      return err(JSMSG_TEMPORAL_PARSER_INVALID_HOUR);
   1085    }
   1086    return result;
   1087  }
   1088  return err(JSMSG_TEMPORAL_PARSER_MISSING_HOUR);
   1089 }
   1090 
   1091 template <typename CharT>
   1092 mozilla::Result<mozilla::Maybe<int32_t>, ParserError>
   1093 TemporalParser<CharT>::minute(bool required) {
   1094  // MinuteSecond :::
   1095  //   0 DecimalDigit
   1096  //   1 DecimalDigit
   1097  //   2 DecimalDigit
   1098  //   3 DecimalDigit
   1099  //   4 DecimalDigit
   1100  //   5 DecimalDigit
   1101  if (auto minute = digits(2)) {
   1102    if (!inBounds(minute.value(), 0, 59)) {
   1103      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MINUTE);
   1104    }
   1105    return minute;
   1106  }
   1107  if (!required) {
   1108    return mozilla::Maybe<int32_t>{mozilla::Nothing{}};
   1109  }
   1110  return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MINUTE);
   1111 }
   1112 
   1113 template <typename CharT>
   1114 mozilla::Result<mozilla::Maybe<int32_t>, ParserError>
   1115 TemporalParser<CharT>::second(bool required) {
   1116  // MinuteSecond :::
   1117  //   0 DecimalDigit
   1118  //   1 DecimalDigit
   1119  //   2 DecimalDigit
   1120  //   3 DecimalDigit
   1121  //   4 DecimalDigit
   1122  //   5 DecimalDigit
   1123  if (auto minute = digits(2)) {
   1124    if (!inBounds(minute.value(), 0, 59)) {
   1125      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_SECOND);
   1126    }
   1127    return minute;
   1128  }
   1129  if (!required) {
   1130    return mozilla::Maybe<int32_t>{mozilla::Nothing{}};
   1131  }
   1132  return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_SECOND);
   1133 }
   1134 
   1135 template <typename CharT>
   1136 mozilla::Result<mozilla::Maybe<int32_t>, ParserError>
   1137 TemporalParser<CharT>::timeSecond(bool required) {
   1138  // TimeSecond :::
   1139  //   MinuteSecond
   1140  //   60
   1141  //
   1142  // MinuteSecond :::
   1143  //   0 DecimalDigit
   1144  //   1 DecimalDigit
   1145  //   2 DecimalDigit
   1146  //   3 DecimalDigit
   1147  //   4 DecimalDigit
   1148  //   5 DecimalDigit
   1149  if (auto minute = digits(2)) {
   1150    if (!inBounds(minute.value(), 0, 60)) {
   1151      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_LEAPSECOND);
   1152    }
   1153    return minute;
   1154  }
   1155  if (!required) {
   1156    return mozilla::Maybe<int32_t>{mozilla::Nothing{}};
   1157  }
   1158  return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_SECOND);
   1159 }
   1160 
   1161 template <typename CharT>
   1162 mozilla::Result<ISODate, ParserError> TemporalParser<CharT>::date() {
   1163  // clang-format off
   1164  //
   1165  // Date :::
   1166  //   DateSpec[+Extended]
   1167  //   DateSpec[~Extended]
   1168  //
   1169  // DateSpec[Extended] :::
   1170  //   DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay
   1171  //
   1172  // clang-format on
   1173 
   1174  ISODate result{};
   1175 
   1176  result.year = MOZ_TRY(dateYear());
   1177 
   1178  // Optional |DateSeparator|.
   1179  bool hasMonthSeparator = dateSeparator();
   1180 
   1181  result.month = MOZ_TRY(dateMonth());
   1182 
   1183  // Optional |DateSeparator|.
   1184  bool hasDaySeparator = dateSeparator();
   1185 
   1186  // Date separators must be consistent.
   1187  if (hasMonthSeparator != hasDaySeparator) {
   1188    return mozilla::Err(JSMSG_TEMPORAL_PARSER_INCONSISTENT_DATE_SEPARATOR);
   1189  }
   1190 
   1191  result.day = MOZ_TRY(dateDay());
   1192 
   1193  return result;
   1194 }
   1195 
   1196 template <typename CharT>
   1197 mozilla::Result<Time, ParserError> TemporalParser<CharT>::time() {
   1198  // clang-format off
   1199  //
   1200  // Time :::
   1201  //   TimeSpec[+Extended]
   1202  //   TimeSpec[~Extended]
   1203  //
   1204  // TimeSpec[Extended] :::
   1205  //   Hour
   1206  //   Hour TimeSeparator[?Extended] MinuteSecond
   1207  //   Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] TimeSecond TemporalDecimalFraction?
   1208  //
   1209  // clang-format on
   1210 
   1211  Time result{};
   1212 
   1213  result.hour = MOZ_TRY(hour());
   1214 
   1215  // Optional |TimeSeparator|.
   1216  bool hasMinuteSeparator = timeSeparator();
   1217 
   1218  mozilla::Maybe<int32_t> minutes = MOZ_TRY(minute(hasMinuteSeparator));
   1219  if (minutes) {
   1220    result.minute = minutes.value();
   1221 
   1222    // Optional |TimeSeparator|.
   1223    bool hasSecondSeparator = timeSeparator();
   1224 
   1225    mozilla::Maybe<int32_t> seconds = MOZ_TRY(timeSecond(hasSecondSeparator));
   1226    if (seconds) {
   1227      result.second = seconds.value();
   1228 
   1229      // Time separators must be consistent.
   1230      if (hasMinuteSeparator != hasSecondSeparator) {
   1231        return mozilla::Err(JSMSG_TEMPORAL_PARSER_INCONSISTENT_TIME_SEPARATOR);
   1232      }
   1233 
   1234      // TemporalDecimalFraction :::
   1235      //   TemporalDecimalSeparator DecimalDigit{1,9}
   1236      if (auto f = fraction()) {
   1237        int32_t fractionalPart = f.value();
   1238        result.millisecond = fractionalPart / 1'000'000;
   1239        result.microsecond = (fractionalPart % 1'000'000) / 1'000;
   1240        result.nanosecond = fractionalPart % 1'000;
   1241      }
   1242    }
   1243  }
   1244 
   1245  return result;
   1246 }
   1247 
   1248 template <typename CharT>
   1249 mozilla::Result<ZonedDateTimeString, ParserError>
   1250 TemporalParser<CharT>::dateTime(bool allowZ) {
   1251  // DateTime[Z, TimeRequired] :::
   1252  //   [~TimeRequired] Date
   1253  //   Date DateTimeSeparator Time DateTimeUTCOffset[?Z]?
   1254  //
   1255  // When called as `DateTime[?Z, ~TimeRequired]`.
   1256 
   1257  ZonedDateTimeString result{};
   1258 
   1259  result.date = MOZ_TRY(date());
   1260 
   1261  if (dateTimeSeparator()) {
   1262    result.time = MOZ_TRY(time());
   1263 
   1264    if (hasDateTimeUTCOffsetStart()) {
   1265      result.timeZone = MOZ_TRY(dateTimeUTCOffset(allowZ));
   1266    }
   1267  } else {
   1268    result.startOfDay = true;
   1269  }
   1270 
   1271  return result;
   1272 }
   1273 
   1274 template <typename CharT>
   1275 mozilla::Result<TimeZoneString, ParserError>
   1276 TemporalParser<CharT>::dateTimeUTCOffset(bool allowZ) {
   1277  // DateTimeUTCOffset[Z] :::
   1278  //   [+Z] UTCDesignator
   1279  //   UTCOffset[+SubMinutePrecision]
   1280 
   1281  if (utcDesignator()) {
   1282    if (!allowZ) {
   1283      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR);
   1284    }
   1285    return TimeZoneString::UTC();
   1286  }
   1287 
   1288  if (hasSign()) {
   1289    DateTimeUTCOffset offset = MOZ_TRY(utcOffsetSubMinutePrecision());
   1290 
   1291    return TimeZoneString::from(offset);
   1292  }
   1293 
   1294  return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE);
   1295 }
   1296 
   1297 template <typename CharT>
   1298 mozilla::Result<TimeZoneUTCOffset, ParserError>
   1299 TemporalParser<CharT>::timeZoneUTCOffsetName() {
   1300  // clang-format off
   1301  //
   1302  // UTCOffset[SubMinutePrecision] :::
   1303  //   ASCIISign Hour
   1304  //   ASCIISign Hour TimeSeparator[+Extended] MinuteSecond
   1305  //   ASCIISign Hour TimeSeparator[~Extended] MinuteSecond
   1306  //   [+SubMinutePrecision] ASCIISign Hour TimeSeparator[+Extended] MinuteSecond TimeSeparator[+Extended] MinuteSecond TemporalDecimalFraction?
   1307  //   [+SubMinutePrecision] ASCIISign Hour TimeSeparator[~Extended] MinuteSecond TimeSeparator[~Extended] MinuteSecond TemporalDecimalFraction?
   1308  //
   1309  // When called as `UTCOffset[~SubMinutePrecision]`.
   1310  //
   1311  // clang-format on
   1312 
   1313  TimeZoneUTCOffset result{};
   1314 
   1315  if (!hasSign()) {
   1316    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_SIGN);
   1317  }
   1318  result.sign = sign();
   1319 
   1320  result.hour = MOZ_TRY(hour());
   1321 
   1322  // Optional |TimeSeparator|.
   1323  bool hasMinuteSeparator = timeSeparator();
   1324 
   1325  mozilla::Maybe<int32_t> minutes = MOZ_TRY(minute(hasMinuteSeparator));
   1326  if (minutes) {
   1327    result.minute = minutes.value();
   1328 
   1329    if (hasTimeSeparator()) {
   1330      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_SUBMINUTE_TIMEZONE);
   1331    }
   1332  }
   1333 
   1334  return result;
   1335 }
   1336 
   1337 template <typename CharT>
   1338 mozilla::Result<DateTimeUTCOffset, ParserError>
   1339 TemporalParser<CharT>::utcOffsetSubMinutePrecision() {
   1340  // clang-format off
   1341  //
   1342  // UTCOffset[SubMinutePrecision] :::
   1343  //   ASCIISign Hour
   1344  //   ASCIISign Hour TimeSeparator[+Extended] MinuteSecond
   1345  //   ASCIISign Hour TimeSeparator[~Extended] MinuteSecond
   1346  //   [+SubMinutePrecision] ASCIISign Hour TimeSeparator[+Extended] MinuteSecond TimeSeparator[+Extended] MinuteSecond TemporalDecimalFraction?
   1347  //   [+SubMinutePrecision] ASCIISign Hour TimeSeparator[~Extended] MinuteSecond TimeSeparator[~Extended] MinuteSecond TemporalDecimalFraction?
   1348  //
   1349  // When called as `UTCOffset[+SubMinutePrecision]`.
   1350  //
   1351  // clang-format on
   1352 
   1353  DateTimeUTCOffset result{};
   1354 
   1355  if (!hasSign()) {
   1356    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_SIGN);
   1357  }
   1358  result.sign = sign();
   1359 
   1360  result.hour = MOZ_TRY(hour());
   1361 
   1362  // Optional |TimeSeparator|.
   1363  bool hasMinuteSeparator = timeSeparator();
   1364 
   1365  mozilla::Maybe<int32_t> minutes = MOZ_TRY(minute(hasMinuteSeparator));
   1366  if (minutes) {
   1367    result.minute = minutes.value();
   1368 
   1369    // Optional |TimeSeparator|.
   1370    bool hasSecondSeparator = timeSeparator();
   1371 
   1372    mozilla::Maybe<int32_t> seconds = MOZ_TRY(second(hasSecondSeparator));
   1373    if (seconds) {
   1374      result.second = seconds.value();
   1375 
   1376      // Time separators must be consistent.
   1377      if (hasMinuteSeparator != hasSecondSeparator) {
   1378        return mozilla::Err(JSMSG_TEMPORAL_PARSER_INCONSISTENT_TIME_SEPARATOR);
   1379      }
   1380 
   1381      if (auto fractionalPart = fraction()) {
   1382        result.fractionalPart = fractionalPart.value();
   1383      }
   1384 
   1385      result.subMinutePrecision = true;
   1386    }
   1387  }
   1388 
   1389  return result;
   1390 }
   1391 
   1392 template <typename CharT>
   1393 mozilla::Result<TimeZoneAnnotation, ParserError>
   1394 TemporalParser<CharT>::timeZoneIdentifier() {
   1395  // TimeZoneIdentifier :::
   1396  //   UTCOffset[~SubMinutePrecision]
   1397  //   TimeZoneIANAName
   1398 
   1399  TimeZoneAnnotation result{};
   1400  if (hasSign()) {
   1401    result.offset = MOZ_TRY(timeZoneUTCOffsetName());
   1402  } else {
   1403    result.name = MOZ_TRY(timeZoneIANAName());
   1404  }
   1405 
   1406  return result;
   1407 }
   1408 
   1409 template <typename CharT>
   1410 mozilla::Result<TimeZoneAnnotation, ParserError>
   1411 TemporalParser<CharT>::timeZoneAnnotation() {
   1412  // TimeZoneAnnotation :::
   1413  //   [ AnnotationCriticalFlag? TimeZoneIdentifier ]
   1414 
   1415  if (!character('[')) {
   1416    return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_TIMEZONE);
   1417  }
   1418 
   1419  // Skip over the optional critical flag.
   1420  annotationCriticalFlag();
   1421 
   1422  auto result = timeZoneIdentifier();
   1423  if (result.isErr()) {
   1424    return result.propagateErr();
   1425  }
   1426 
   1427  if (!character(']')) {
   1428    return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_TIMEZONE);
   1429  }
   1430 
   1431  return result;
   1432 }
   1433 
   1434 template <typename CharT>
   1435 mozilla::Result<TimeZoneName, ParserError>
   1436 TemporalParser<CharT>::timeZoneIANAName() {
   1437  // TimeZoneIANAName :::
   1438  //   TimeZoneIANANameComponent
   1439  //   TimeZoneIANAName / TimeZoneIANANameComponent
   1440  //
   1441  // TimeZoneIANANameComponent :::
   1442  //   TZLeadingChar
   1443  //   TimeZoneIANANameComponent TZChar
   1444 
   1445  size_t start = reader_.index();
   1446 
   1447  do {
   1448    if (!tzLeadingChar()) {
   1449      return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_NAME);
   1450    }
   1451 
   1452    // Optionally followed by a sequence of |TZChar|.
   1453    while (tzChar()) {
   1454    }
   1455  } while (character('/'));
   1456 
   1457  return TimeZoneName{start, reader_.index() - start};
   1458 }
   1459 
   1460 template <typename CharT>
   1461 mozilla::Maybe<double> TemporalParser<CharT>::digits(JSContext* cx) {
   1462  auto span = reader_.string().Subspan(reader_.index());
   1463 
   1464  // GetPrefixInteger can't fail when integer separator handling is disabled.
   1465  const CharT* endp = nullptr;
   1466  double num;
   1467  MOZ_ALWAYS_TRUE(GetPrefixInteger(span.data(), span.data() + span.size(), 10,
   1468                                   IntegerSeparatorHandling::None, &endp,
   1469                                   &num));
   1470 
   1471  size_t len = endp - span.data();
   1472  if (len == 0) {
   1473    return mozilla::Nothing();
   1474  }
   1475  reader_.advance(len);
   1476  return mozilla::Some(num);
   1477 }
   1478 
   1479 template <typename CharT>
   1480 mozilla::Result<ZonedDateTimeString, ParserError>
   1481 TemporalParser<CharT>::parseTemporalInstantString() {
   1482  MOZ_TRY(nonempty());
   1483 
   1484  // Initialize all fields to zero.
   1485  ZonedDateTimeString result{};
   1486 
   1487  // clang-format off
   1488  //
   1489  // TemporalInstantString :::
   1490  //   Date DateTimeSeparator Time DateTimeUTCOffset[+Z] TimeZoneAnnotation? Annotations?
   1491  //
   1492  // clang-format on
   1493 
   1494  result.date = MOZ_TRY(date());
   1495 
   1496  if (!dateTimeSeparator()) {
   1497    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DATE_TIME_SEPARATOR);
   1498  }
   1499 
   1500  result.time = MOZ_TRY(time());
   1501 
   1502  result.timeZone = MOZ_TRY(dateTimeUTCOffset(/* allowZ = */ true));
   1503 
   1504  if (hasTimeZoneAnnotationStart()) {
   1505    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   1506  }
   1507 
   1508  if (hasAnnotationStart()) {
   1509    MOZ_TRY(annotations());
   1510  }
   1511 
   1512  return complete(result);
   1513 }
   1514 
   1515 /**
   1516 * ParseTemporalInstantString ( isoString )
   1517 */
   1518 template <typename CharT>
   1519 static auto ParseTemporalInstantString(mozilla::Span<const CharT> str) {
   1520  TemporalParser<CharT> parser(str);
   1521  return parser.parseTemporalInstantString();
   1522 }
   1523 
   1524 /**
   1525 * ParseTemporalInstantString ( isoString )
   1526 */
   1527 static auto ParseTemporalInstantString(Handle<JSLinearString*> str) {
   1528  JS::AutoCheckCannotGC nogc;
   1529  if (str->hasLatin1Chars()) {
   1530    return ParseTemporalInstantString<Latin1Char>(str->latin1Range(nogc));
   1531  }
   1532  return ParseTemporalInstantString<char16_t>(str->twoByteRange(nogc));
   1533 }
   1534 
   1535 /**
   1536 * ParseTemporalInstantString ( isoString )
   1537 */
   1538 bool js::temporal::ParseTemporalInstantString(JSContext* cx,
   1539                                              Handle<JSString*> str,
   1540                                              ISODateTime* result,
   1541                                              int64_t* offset) {
   1542  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   1543  if (!linear) {
   1544    return false;
   1545  }
   1546 
   1547  // Step 1.
   1548  auto parseResult = ::ParseTemporalInstantString(linear);
   1549  if (parseResult.isErr()) {
   1550    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1551                              parseResult.unwrapErr(), "instant");
   1552    return false;
   1553  }
   1554  ZonedDateTimeString parsed = parseResult.unwrap();
   1555 
   1556  // Step 2.
   1557  if (!ParseISODateTime(cx, parsed, result)) {
   1558    return false;
   1559  }
   1560 
   1561  // Steps 3-4.
   1562  if (parsed.timeZone.hasOffset()) {
   1563    *offset = ParseDateTimeUTCOffset(parsed.timeZone.offset).offset;
   1564  } else {
   1565    MOZ_ASSERT(parsed.timeZone.isUTC());
   1566    *offset = 0;
   1567  }
   1568  return true;
   1569 }
   1570 
   1571 template <typename CharT>
   1572 mozilla::Result<ZonedDateTimeString, ParserError>
   1573 TemporalParser<CharT>::parseTemporalTimeZoneString() {
   1574  MOZ_TRY(nonempty());
   1575 
   1576  // Handle the common case of a standalone time zone identifier first.
   1577  if (auto tz = parse(timeZoneIdentifier()); tz.isOk()) {
   1578    auto timeZone = tz.unwrap();
   1579 
   1580    ZonedDateTimeString result{};
   1581    if (timeZone.hasOffset()) {
   1582      result.timeZone = TimeZoneString::from(timeZone.offset);
   1583    } else {
   1584      MOZ_ASSERT(timeZone.hasName());
   1585      result.timeZone = TimeZoneString::from(timeZone.name);
   1586    }
   1587    return result;
   1588  }
   1589 
   1590  LikelyError likelyError{};
   1591 
   1592  // Try all five parse goals from ParseISODateTime in order.
   1593  //
   1594  // TemporalDateTimeString
   1595  // TemporalInstantString
   1596  // TemporalTimeString
   1597  // TemporalMonthDayString
   1598  // TemporalYearMonthString
   1599 
   1600  // Restart parsing from the start of the string.
   1601  reader_.reset();
   1602 
   1603  auto dateTime = parseTemporalDateTimeString();
   1604  if (dateTime.isOk()) {
   1605    return dateTime;
   1606  }
   1607  likelyError.update(dateTime, reader_.index());
   1608 
   1609  // Restart parsing from the start of the string.
   1610  reader_.reset();
   1611 
   1612  auto instant = parseTemporalInstantString();
   1613  if (instant.isOk()) {
   1614    return instant;
   1615  }
   1616  likelyError.update(instant, reader_.index());
   1617 
   1618  // Restart parsing from the start of the string.
   1619  reader_.reset();
   1620 
   1621  auto time = parseTemporalTimeString();
   1622  if (time.isOk()) {
   1623    return time;
   1624  }
   1625  likelyError.update(time, reader_.index());
   1626 
   1627  // Restart parsing from the start of the string.
   1628  reader_.reset();
   1629 
   1630  auto monthDay = parseTemporalMonthDayString();
   1631  if (monthDay.isOk()) {
   1632    return monthDay;
   1633  }
   1634  likelyError.update(monthDay, reader_.index());
   1635 
   1636  // Restart parsing from the start of the string.
   1637  reader_.reset();
   1638 
   1639  auto yearMonth = parseTemporalYearMonthString();
   1640  if (yearMonth.isOk()) {
   1641    return yearMonth;
   1642  }
   1643  likelyError.update(yearMonth, reader_.index());
   1644 
   1645  return likelyError.propagate();
   1646 }
   1647 
   1648 /**
   1649 * ParseTemporalTimeZoneString ( timeZoneString )
   1650 */
   1651 template <typename CharT>
   1652 static auto ParseTemporalTimeZoneString(mozilla::Span<const CharT> str) {
   1653  TemporalParser<CharT> parser(str);
   1654  return parser.parseTemporalTimeZoneString();
   1655 }
   1656 
   1657 /**
   1658 * ParseTemporalTimeZoneString ( timeZoneString )
   1659 */
   1660 static auto ParseTemporalTimeZoneString(Handle<JSLinearString*> str) {
   1661  JS::AutoCheckCannotGC nogc;
   1662  if (str->hasLatin1Chars()) {
   1663    return ParseTemporalTimeZoneString<Latin1Char>(str->latin1Range(nogc));
   1664  }
   1665  return ParseTemporalTimeZoneString<char16_t>(str->twoByteRange(nogc));
   1666 }
   1667 
   1668 /**
   1669 * ParseTemporalTimeZoneString ( timeZoneString )
   1670 */
   1671 bool js::temporal::ParseTemporalTimeZoneString(
   1672    JSContext* cx, Handle<JSString*> str,
   1673    MutableHandle<ParsedTimeZone> result) {
   1674  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   1675  if (!linear) {
   1676    return false;
   1677  }
   1678 
   1679  // Steps 1-4.
   1680  auto parseResult = ::ParseTemporalTimeZoneString(linear);
   1681  if (parseResult.isErr()) {
   1682    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1683                              parseResult.unwrapErr(), "time zone");
   1684    return false;
   1685  }
   1686  ZonedDateTimeString parsed = parseResult.unwrap();
   1687  const auto& timeZone = parsed.timeZone;
   1688 
   1689  // Step 3.
   1690  ISODateTime unused;
   1691  if (!ParseISODateTime(cx, parsed, &unused)) {
   1692    return false;
   1693  }
   1694 
   1695  if (timeZone.hasAnnotation()) {
   1696    // Case 1: 19700101T00:00Z[+02:00]
   1697    // Case 2: 19700101T00:00+00:00[+02:00]
   1698    // Case 3: 19700101T00:00[+02:00]
   1699    // Case 4: 19700101T00:00Z[Europe/Berlin]
   1700    // Case 5: 19700101T00:00+00:00[Europe/Berlin]
   1701    // Case 6: 19700101T00:00[Europe/Berlin]
   1702 
   1703    if (!ParseTimeZoneAnnotation(cx, timeZone.annotation, linear, result)) {
   1704      return false;
   1705    }
   1706  } else if (timeZone.isUTC()) {
   1707    result.set(ParsedTimeZone::fromName(cx->names().UTC));
   1708  } else if (timeZone.hasOffset()) {
   1709    // ToTemporalTimeZoneSlotValue, step 7.
   1710    //
   1711    // Error reporting for sub-minute precision moved here.
   1712    if (timeZone.offset.subMinutePrecision) {
   1713      JS_ReportErrorNumberASCII(
   1714          cx, GetErrorMessage, nullptr,
   1715          JSMSG_TEMPORAL_PARSER_INVALID_SUBMINUTE_TIMEZONE, "time zone");
   1716      return false;
   1717    }
   1718 
   1719    int32_t offset = ParseTimeZoneOffset(timeZone.offset.toTimeZoneUTCOffset());
   1720    result.set(ParsedTimeZone::fromOffset(offset));
   1721  } else {
   1722    // Step 5.
   1723    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1724                              JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE,
   1725                              "time zone");
   1726    return false;
   1727  }
   1728 
   1729  // Step 6.
   1730  return true;
   1731 }
   1732 
   1733 template <typename CharT>
   1734 mozilla::Result<TimeZoneAnnotation, ParserError>
   1735 TemporalParser<CharT>::parseTimeZoneIdentifier() {
   1736  MOZ_TRY(nonempty());
   1737  return parse(timeZoneIdentifier());
   1738 }
   1739 
   1740 /**
   1741 * ParseTimeZoneIdentifier ( identifier )
   1742 */
   1743 template <typename CharT>
   1744 static auto ParseTimeZoneIdentifier(mozilla::Span<const CharT> str) {
   1745  TemporalParser<CharT> parser(str);
   1746  return parser.parseTimeZoneIdentifier();
   1747 }
   1748 
   1749 /**
   1750 * ParseTimeZoneIdentifier ( identifier )
   1751 */
   1752 static auto ParseTimeZoneIdentifier(Handle<JSLinearString*> str) {
   1753  JS::AutoCheckCannotGC nogc;
   1754  if (str->hasLatin1Chars()) {
   1755    return ParseTimeZoneIdentifier<Latin1Char>(str->latin1Range(nogc));
   1756  }
   1757  return ParseTimeZoneIdentifier<char16_t>(str->twoByteRange(nogc));
   1758 }
   1759 
   1760 /**
   1761 * ParseTimeZoneIdentifier ( identifier )
   1762 */
   1763 bool js::temporal::ParseTimeZoneIdentifier(
   1764    JSContext* cx, Handle<JSString*> str,
   1765    MutableHandle<ParsedTimeZone> result) {
   1766  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   1767  if (!linear) {
   1768    return false;
   1769  }
   1770 
   1771  // Steps 1-2.
   1772  auto parseResult = ::ParseTimeZoneIdentifier(linear);
   1773  if (parseResult.isErr()) {
   1774    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1775                              parseResult.unwrapErr(), "time zone identifier");
   1776    return false;
   1777  }
   1778  auto timeZone = parseResult.unwrap();
   1779 
   1780  // Steps 3-4.
   1781  return ParseTimeZoneAnnotation(cx, timeZone, linear, result);
   1782 }
   1783 
   1784 template <typename CharT>
   1785 mozilla::Result<DateTimeUTCOffset, ParserError>
   1786 TemporalParser<CharT>::parseDateTimeUTCOffset() {
   1787  MOZ_TRY(nonempty());
   1788  return parse(utcOffsetSubMinutePrecision());
   1789 }
   1790 
   1791 /**
   1792 * ParseDateTimeUTCOffset ( offsetString )
   1793 */
   1794 template <typename CharT>
   1795 static auto ParseDateTimeUTCOffset(mozilla::Span<const CharT> str) {
   1796  TemporalParser<CharT> parser(str);
   1797  return parser.parseDateTimeUTCOffset();
   1798 }
   1799 
   1800 /**
   1801 * ParseDateTimeUTCOffset ( offsetString )
   1802 */
   1803 static auto ParseDateTimeUTCOffset(Handle<JSLinearString*> str) {
   1804  JS::AutoCheckCannotGC nogc;
   1805  if (str->hasLatin1Chars()) {
   1806    return ParseDateTimeUTCOffset<Latin1Char>(str->latin1Range(nogc));
   1807  }
   1808  return ParseDateTimeUTCOffset<char16_t>(str->twoByteRange(nogc));
   1809 }
   1810 
   1811 /**
   1812 * ParseDateTimeUTCOffset ( offsetString )
   1813 */
   1814 bool js::temporal::ParseDateTimeUTCOffset(JSContext* cx, Handle<JSString*> str,
   1815                                          int64_t* result) {
   1816  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   1817  if (!linear) {
   1818    return false;
   1819  }
   1820 
   1821  // Steps 1-2.
   1822  auto parseResult = ::ParseDateTimeUTCOffset(linear);
   1823  if (parseResult.isErr()) {
   1824    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1825                              parseResult.unwrapErr(), "UTC offset");
   1826    return false;
   1827  }
   1828 
   1829  // Steps 3-21.
   1830  *result = ParseDateTimeUTCOffset(parseResult.unwrap()).offset;
   1831  return true;
   1832 }
   1833 
   1834 template <typename CharT>
   1835 mozilla::Result<double, ParserError> TemporalParser<CharT>::durationDigits(
   1836    JSContext* cx) {
   1837  auto d = digits(cx);
   1838  if (!d) {
   1839    return err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS);
   1840  }
   1841  return *d;
   1842 }
   1843 
   1844 template <typename CharT>
   1845 mozilla::Result<TemporalDurationString, ParserError>
   1846 TemporalParser<CharT>::parseTemporalDurationString(JSContext* cx) {
   1847  MOZ_TRY(nonempty());
   1848 
   1849  // Initialize all fields to zero.
   1850  TemporalDurationString result{};
   1851 
   1852  // TemporalDurationString :::
   1853  //   Duration
   1854  //
   1855  // Duration :::
   1856  //   ASCIISign? DurationDesignator DurationDate
   1857  //   ASCIISign? DurationDesignator DurationTime
   1858 
   1859  if (hasSign()) {
   1860    result.sign = sign();
   1861  }
   1862 
   1863  if (!durationDesignator()) {
   1864    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DESIGNATOR);
   1865  }
   1866 
   1867  // DurationDate :::
   1868  //   DurationYearsPart DurationTime?
   1869  //   DurationMonthsPart DurationTime?
   1870  //   DurationWeeksPart DurationTime?
   1871  //   DurationDaysPart DurationTime?
   1872 
   1873  do {
   1874    if (hasTimeDesignator()) {
   1875      break;
   1876    }
   1877 
   1878    double num = MOZ_TRY(durationDigits(cx));
   1879 
   1880    // DurationYearsPart :::
   1881    //   DecimalDigits[~Sep] YearsDesignator DurationMonthsPart
   1882    //   DecimalDigits[~Sep] YearsDesignator DurationWeeksPart
   1883    //   DecimalDigits[~Sep] YearsDesignator DurationDaysPart?
   1884    if (yearsDesignator()) {
   1885      result.years = num;
   1886      if (reader_.atEnd()) {
   1887        return result;
   1888      }
   1889      if (hasTimeDesignator()) {
   1890        break;
   1891      }
   1892      num = MOZ_TRY(durationDigits(cx));
   1893    }
   1894 
   1895    // DurationMonthsPart :::
   1896    //   DecimalDigits[~Sep] MonthsDesignator DurationWeeksPart
   1897    //   DecimalDigits[~Sep] MonthsDesignator DurationDaysPart?
   1898    if (monthsDesignator()) {
   1899      result.months = num;
   1900      if (reader_.atEnd()) {
   1901        return result;
   1902      }
   1903      if (hasTimeDesignator()) {
   1904        break;
   1905      }
   1906      num = MOZ_TRY(durationDigits(cx));
   1907    }
   1908 
   1909    // DurationWeeksPart :::
   1910    //   DecimalDigits[~Sep] WeeksDesignator DurationDaysPart?
   1911    if (weeksDesignator()) {
   1912      result.weeks = num;
   1913      if (reader_.atEnd()) {
   1914        return result;
   1915      }
   1916      if (hasTimeDesignator()) {
   1917        break;
   1918      }
   1919      num = MOZ_TRY(durationDigits(cx));
   1920    }
   1921 
   1922    // DurationDaysPart :::
   1923    //   DecimalDigits[~Sep] DaysDesignator
   1924    if (daysDesignator()) {
   1925      result.days = num;
   1926      if (reader_.atEnd()) {
   1927        return result;
   1928      }
   1929      if (hasTimeDesignator()) {
   1930        break;
   1931      }
   1932    }
   1933 
   1934    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_UNIT_DESIGNATOR);
   1935  } while (false);
   1936 
   1937  // DurationTime :::
   1938  //   TimeDesignator DurationHoursPart
   1939  //   TimeDesignator DurationMinutesPart
   1940  //   TimeDesignator DurationSecondsPart
   1941  if (!timeDesignator()) {
   1942    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIME_DESIGNATOR);
   1943  }
   1944 
   1945  double num = MOZ_TRY(durationDigits(cx));
   1946 
   1947  auto frac = fraction();
   1948 
   1949  // DurationHoursPart :::
   1950  //   DecimalDigits[~Sep] TemporalDecimalFraction HoursDesignator
   1951  //   DecimalDigits[~Sep] HoursDesignator DurationMinutesPart
   1952  //   DecimalDigits[~Sep] HoursDesignator DurationSecondsPart?
   1953  bool hasHoursFraction = false;
   1954  if (hoursDesignator()) {
   1955    hasHoursFraction = bool(frac);
   1956    result.hours = num;
   1957    result.hoursFraction = frac.valueOr(0);
   1958    if (reader_.atEnd()) {
   1959      return result;
   1960    }
   1961 
   1962    num = MOZ_TRY(durationDigits(cx));
   1963    frac = fraction();
   1964  }
   1965 
   1966  // DurationMinutesPart :::
   1967  //   DecimalDigits[~Sep] TemporalDecimalFraction MinutesDesignator
   1968  //   DecimalDigits[~Sep] MinutesDesignator DurationSecondsPart?
   1969  bool hasMinutesFraction = false;
   1970  if (minutesDesignator()) {
   1971    if (hasHoursFraction) {
   1972      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_MINUTES);
   1973    }
   1974    hasMinutesFraction = bool(frac);
   1975    result.minutes = num;
   1976    result.minutesFraction = frac.valueOr(0);
   1977    if (reader_.atEnd()) {
   1978      return result;
   1979    }
   1980 
   1981    num = MOZ_TRY(durationDigits(cx));
   1982    frac = fraction();
   1983  }
   1984 
   1985  // DurationSecondsPart :::
   1986  //   DecimalDigits[~Sep] TemporalDecimalFraction? SecondsDesignator
   1987  if (secondsDesignator()) {
   1988    if (hasHoursFraction || hasMinutesFraction) {
   1989      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_SECONDS);
   1990    }
   1991    result.seconds = num;
   1992    result.secondsFraction = frac.valueOr(0);
   1993    if (reader_.atEnd()) {
   1994      return result;
   1995    }
   1996  }
   1997 
   1998  return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_UNIT_DESIGNATOR);
   1999 }
   2000 
   2001 /**
   2002 * ParseTemporalDurationString ( isoString )
   2003 */
   2004 template <typename CharT>
   2005 static auto ParseTemporalDurationString(JSContext* cx,
   2006                                        mozilla::Span<const CharT> str) {
   2007  TemporalParser<CharT> parser(str);
   2008  return parser.parseTemporalDurationString(cx);
   2009 }
   2010 
   2011 /**
   2012 * ParseTemporalDurationString ( isoString )
   2013 */
   2014 static auto ParseTemporalDurationString(JSContext* cx,
   2015                                        Handle<JSLinearString*> str) {
   2016  JS::AutoCheckCannotGC nogc;
   2017  if (str->hasLatin1Chars()) {
   2018    return ParseTemporalDurationString<Latin1Char>(cx, str->latin1Range(nogc));
   2019  }
   2020  return ParseTemporalDurationString<char16_t>(cx, str->twoByteRange(nogc));
   2021 }
   2022 
   2023 /**
   2024 * ParseTemporalDurationString ( isoString )
   2025 */
   2026 bool js::temporal::ParseTemporalDurationString(JSContext* cx,
   2027                                               Handle<JSString*> str,
   2028                                               Duration* result) {
   2029  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2030  if (!linear) {
   2031    return false;
   2032  }
   2033 
   2034  // Steps 1-3.
   2035  auto parseResult = ::ParseTemporalDurationString(cx, linear);
   2036  if (parseResult.isErr()) {
   2037    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2038                              parseResult.unwrapErr(), "duration");
   2039    return false;
   2040  }
   2041  TemporalDurationString parsed = parseResult.unwrap();
   2042 
   2043  // Steps 4-8.
   2044  double years = parsed.years;
   2045  double months = parsed.months;
   2046  double weeks = parsed.weeks;
   2047  double days = parsed.days;
   2048  double hours = parsed.hours;
   2049 
   2050  // Steps 9-17.
   2051  double minutes, seconds, milliseconds, microseconds, nanoseconds;
   2052  if (parsed.hoursFraction) {
   2053    MOZ_ASSERT(parsed.hoursFraction > 0);
   2054    MOZ_ASSERT(parsed.hoursFraction < 1'000'000'000);
   2055 
   2056    // Step 9.a.
   2057    MOZ_ASSERT(parsed.minutes == 0);
   2058    MOZ_ASSERT(parsed.minutesFraction == 0);
   2059    MOZ_ASSERT(parsed.seconds == 0);
   2060    MOZ_ASSERT(parsed.secondsFraction == 0);
   2061 
   2062    // Steps 9.b-d.
   2063    int64_t h = int64_t(parsed.hoursFraction) * 60;
   2064    minutes = double(h / 1'000'000'000);
   2065 
   2066    // Steps 13 and 15-17.
   2067    int64_t min = (h % 1'000'000'000) * 60;
   2068    seconds = double(min / 1'000'000'000);
   2069    milliseconds = double((min % 1'000'000'000) / 1'000'000);
   2070    microseconds = double((min % 1'000'000) / 1'000);
   2071    nanoseconds = double(min % 1'000);
   2072  }
   2073 
   2074  // Step 11.
   2075  else if (parsed.minutesFraction) {
   2076    MOZ_ASSERT(parsed.minutesFraction > 0);
   2077    MOZ_ASSERT(parsed.minutesFraction < 1'000'000'000);
   2078 
   2079    // Step 11.a.
   2080    MOZ_ASSERT(parsed.seconds == 0);
   2081    MOZ_ASSERT(parsed.secondsFraction == 0);
   2082 
   2083    // Step 10.
   2084    minutes = parsed.minutes;
   2085 
   2086    // Steps 11.b-d and 15-17.
   2087    int64_t min = int64_t(parsed.minutesFraction) * 60;
   2088    seconds = double(min / 1'000'000'000);
   2089    milliseconds = double((min % 1'000'000'000) / 1'000'000);
   2090    microseconds = double((min % 1'000'000) / 1'000);
   2091    nanoseconds = double(min % 1'000);
   2092  }
   2093 
   2094  // Step 14.
   2095  else if (parsed.secondsFraction) {
   2096    MOZ_ASSERT(parsed.secondsFraction > 0);
   2097    MOZ_ASSERT(parsed.secondsFraction < 1'000'000'000);
   2098 
   2099    // Step 10.
   2100    minutes = parsed.minutes;
   2101 
   2102    // Step 12.
   2103    seconds = parsed.seconds;
   2104 
   2105    // Steps 14, 16-17
   2106    milliseconds = double(parsed.secondsFraction / 1'000'000);
   2107    microseconds = double((parsed.secondsFraction % 1'000'000) / 1'000);
   2108    nanoseconds = double(parsed.secondsFraction % 1'000);
   2109  } else {
   2110    // Step 10.
   2111    minutes = parsed.minutes;
   2112 
   2113    // Step 12.
   2114    seconds = parsed.seconds;
   2115 
   2116    // Steps 15-17
   2117    milliseconds = 0;
   2118    microseconds = 0;
   2119    nanoseconds = 0;
   2120  }
   2121 
   2122  // Steps 18-19.
   2123  int32_t factor = parsed.sign ? parsed.sign : 1;
   2124  MOZ_ASSERT(factor == -1 || factor == 1);
   2125 
   2126  // Steps 20-29.
   2127  *result = {
   2128      (years * factor) + (+0.0),        (months * factor) + (+0.0),
   2129      (weeks * factor) + (+0.0),        (days * factor) + (+0.0),
   2130      (hours * factor) + (+0.0),        (minutes * factor) + (+0.0),
   2131      (seconds * factor) + (+0.0),      (milliseconds * factor) + (+0.0),
   2132      (microseconds * factor) + (+0.0), (nanoseconds * factor) + (+0.0),
   2133  };
   2134 
   2135  // Steps 30-31.
   2136  return ThrowIfInvalidDuration(cx, *result);
   2137 }
   2138 
   2139 template <typename CharT>
   2140 mozilla::Result<AnnotationKey, ParserError>
   2141 TemporalParser<CharT>::annotationKey() {
   2142  // AnnotationKey :::
   2143  //    AKeyLeadingChar
   2144  //    AnnotationKey AKeyChar
   2145 
   2146  size_t start = reader_.index();
   2147 
   2148  if (!aKeyLeadingChar()) {
   2149    return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_KEY);
   2150  }
   2151 
   2152  // Optionally followed by a sequence of |AKeyChar|.
   2153  while (aKeyChar()) {
   2154  }
   2155 
   2156  return AnnotationKey{start, reader_.index() - start};
   2157 }
   2158 
   2159 template <typename CharT>
   2160 mozilla::Result<AnnotationValue, ParserError>
   2161 TemporalParser<CharT>::annotationValue() {
   2162  // AnnotationValue :::
   2163  //   AnnotationValueComponent
   2164  //   AnnotationValueComponent - AnnotationValue
   2165 
   2166  size_t start = reader_.index();
   2167 
   2168  do {
   2169    if (!annotationValueComponent()) {
   2170      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_VALUE);
   2171    }
   2172  } while (character('-'));
   2173 
   2174  return AnnotationValue{start, reader_.index() - start};
   2175 }
   2176 
   2177 template <typename CharT>
   2178 mozilla::Result<Annotation, ParserError> TemporalParser<CharT>::annotation() {
   2179  // Annotation :::
   2180  //   [ AnnotationCriticalFlag? AnnotationKey = AnnotationValue ]
   2181 
   2182  if (!character('[')) {
   2183    return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_ANNOTATION);
   2184  }
   2185 
   2186  Annotation result{};
   2187 
   2188  result.critical = annotationCriticalFlag();
   2189 
   2190  result.key = MOZ_TRY(annotationKey());
   2191 
   2192  if (!character('=')) {
   2193    return mozilla::Err(JSMSG_TEMPORAL_PARSER_ASSIGNMENT_IN_ANNOTATION);
   2194  }
   2195 
   2196  result.value = MOZ_TRY(annotationValue());
   2197 
   2198  if (!character(']')) {
   2199    return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_ANNOTATION);
   2200  }
   2201 
   2202  return result;
   2203 }
   2204 
   2205 template <typename CharT>
   2206 mozilla::Result<CalendarName, ParserError>
   2207 TemporalParser<CharT>::annotations() {
   2208  // Annotations :::
   2209  //   Annotation Annotations?
   2210 
   2211  MOZ_ASSERT(hasAnnotationStart());
   2212 
   2213  CalendarName calendar;
   2214  bool calendarWasCritical = false;
   2215  while (hasAnnotationStart()) {
   2216    Annotation anno = MOZ_TRY(annotation());
   2217 
   2218    auto [key, value, critical] = anno;
   2219 
   2220    static constexpr std::string_view ca = "u-ca";
   2221 
   2222    auto keySpan = reader_.substring(key);
   2223    if (keySpan.size() == ca.length() &&
   2224        std::equal(ca.begin(), ca.end(), keySpan.data())) {
   2225      if (!calendar.present()) {
   2226        calendar = value;
   2227        calendarWasCritical = critical;
   2228      } else if (critical || calendarWasCritical) {
   2229        return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_CRITICAL_ANNOTATION);
   2230      }
   2231    } else if (critical) {
   2232      return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_CRITICAL_ANNOTATION);
   2233    }
   2234  }
   2235  return calendar;
   2236 }
   2237 
   2238 template <typename CharT>
   2239 mozilla::Result<ZonedDateTimeString, ParserError>
   2240 TemporalParser<CharT>::annotatedTime() {
   2241  // clang-format off
   2242  //
   2243  // AnnotatedTime :::
   2244  //   TimeDesignator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations?
   2245  //   Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations?
   2246  //
   2247  // clang-format on
   2248 
   2249  ZonedDateTimeString result{};
   2250 
   2251  size_t start = reader_.index();
   2252  bool hasTimeDesignator = timeDesignator();
   2253 
   2254  result.time = MOZ_TRY(time());
   2255 
   2256  if (hasDateTimeUTCOffsetStart()) {
   2257    result.timeZone = MOZ_TRY(dateTimeUTCOffset(/* allowZ = */ false));
   2258  }
   2259 
   2260  // Early error if `Time DateTimeUTCOffset[~Z]` can be parsed as either
   2261  // `DateSpecMonthDay` or `DateSpecYearMonth`.
   2262  if (!hasTimeDesignator) {
   2263    size_t end = reader_.index();
   2264 
   2265    auto isValidMonthDay = [](const ISODate& date) {
   2266      MOZ_ASSERT(date.year == AbsentYear);
   2267      MOZ_ASSERT(1 <= date.month && date.month <= 12);
   2268      MOZ_ASSERT(1 <= date.day && date.day <= 31);
   2269 
   2270      constexpr int32_t leapYear = 0;
   2271      return date.day <= ISODaysInMonth(leapYear, date.month);
   2272    };
   2273 
   2274    // Reset and check if the input can also be parsed as DateSpecMonthDay.
   2275    reader_.reset(start);
   2276 
   2277    if (auto monthDay = dateSpecMonthDay(); monthDay.isOk()) {
   2278      if (reader_.index() == end && isValidMonthDay(monthDay.unwrap())) {
   2279        return mozilla::Err(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_MONTH_DAY);
   2280      }
   2281    }
   2282 
   2283    // Reset and check if the input can also be parsed as DateSpecYearMonth.
   2284    reader_.reset(start);
   2285 
   2286    if (dateSpecYearMonth().isOk()) {
   2287      if (reader_.index() == end) {
   2288        return mozilla::Err(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_YEAR_MONTH);
   2289      }
   2290    }
   2291 
   2292    // Input can neither be parsed as DateSpecMonthDay nor DateSpecYearMonth.
   2293    reader_.reset(end);
   2294  }
   2295 
   2296  if (hasTimeZoneAnnotationStart()) {
   2297    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2298  }
   2299 
   2300  if (hasAnnotationStart()) {
   2301    result.calendar = MOZ_TRY(annotations());
   2302  }
   2303 
   2304  return result;
   2305 }
   2306 
   2307 template <typename CharT>
   2308 mozilla::Result<ZonedDateTimeString, ParserError>
   2309 TemporalParser<CharT>::annotatedDateTime() {
   2310  // AnnotatedDateTime[Zoned, TimeRequired] :::
   2311  //  [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations?
   2312  //  [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations?
   2313  //
   2314  // When called as `AnnotatedDateTime[~Zoned, ~TimeRequired]`.
   2315 
   2316  ZonedDateTimeString result = MOZ_TRY(dateTime(/* allowZ = */ false));
   2317 
   2318  if (hasTimeZoneAnnotationStart()) {
   2319    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2320  }
   2321 
   2322  if (hasAnnotationStart()) {
   2323    result.calendar = MOZ_TRY(annotations());
   2324  }
   2325 
   2326  return result;
   2327 }
   2328 
   2329 template <typename CharT>
   2330 mozilla::Result<ZonedDateTimeString, ParserError>
   2331 TemporalParser<CharT>::annotatedDateTimeTimeRequired() {
   2332  // AnnotatedDateTime[Zoned, TimeRequired] :::
   2333  //  [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations?
   2334  //  [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations?
   2335  //
   2336  // DateTime[Z, TimeRequired] :::
   2337  //   [~TimeRequired] Date
   2338  //   Date DateTimeSeparator Time DateTimeUTCOffset[?Z]?
   2339  //
   2340  // When called as `AnnotatedDateTime[~Zoned, +TimeRequired]`.
   2341 
   2342  ZonedDateTimeString result{};
   2343 
   2344  result.date = MOZ_TRY(date());
   2345 
   2346  if (!dateTimeSeparator()) {
   2347    return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DATE_TIME_SEPARATOR);
   2348  }
   2349 
   2350  result.time = MOZ_TRY(time());
   2351 
   2352  if (hasDateTimeUTCOffsetStart()) {
   2353    result.timeZone = MOZ_TRY(dateTimeUTCOffset(/* allowZ = */ false));
   2354  }
   2355 
   2356  if (hasTimeZoneAnnotationStart()) {
   2357    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2358  }
   2359 
   2360  if (hasAnnotationStart()) {
   2361    result.calendar = MOZ_TRY(annotations());
   2362  }
   2363 
   2364  return result;
   2365 }
   2366 
   2367 template <typename CharT>
   2368 mozilla::Result<ZonedDateTimeString, ParserError>
   2369 TemporalParser<CharT>::annotatedYearMonth() {
   2370  // AnnotatedYearMonth :::
   2371  //   DateSpecYearMonth TimeZoneAnnotation? Annotations?
   2372 
   2373  ZonedDateTimeString result{};
   2374 
   2375  result.date = MOZ_TRY(dateSpecYearMonth());
   2376 
   2377  if (hasTimeZoneAnnotationStart()) {
   2378    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2379  }
   2380 
   2381  if (hasAnnotationStart()) {
   2382    result.calendar = MOZ_TRY(annotations());
   2383  }
   2384 
   2385  return result;
   2386 }
   2387 
   2388 template <typename CharT>
   2389 mozilla::Result<ZonedDateTimeString, ParserError>
   2390 TemporalParser<CharT>::annotatedMonthDay() {
   2391  // AnnotatedMonthDay :::
   2392  //   DateSpecMonthDay TimeZoneAnnotation? Annotations?
   2393 
   2394  ZonedDateTimeString result{};
   2395 
   2396  result.date = MOZ_TRY(dateSpecMonthDay());
   2397 
   2398  if (hasTimeZoneAnnotationStart()) {
   2399    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2400  }
   2401 
   2402  if (hasAnnotationStart()) {
   2403    result.calendar = MOZ_TRY(annotations());
   2404  }
   2405 
   2406  return result;
   2407 }
   2408 
   2409 template <typename CharT>
   2410 mozilla::Result<ISODate, ParserError>
   2411 TemporalParser<CharT>::dateSpecYearMonth() {
   2412  // DateSpecYearMonth :::
   2413  //   DateYear DateSeparator[+Extended] DateMonth
   2414  //   DateYear DateSeparator[~Extended] DateMonth
   2415 
   2416  ISODate result{};
   2417 
   2418  result.year = MOZ_TRY(dateYear());
   2419 
   2420  // Optional |DateSeparator|.
   2421  dateSeparator();
   2422 
   2423  result.month = MOZ_TRY(dateMonth());
   2424 
   2425  return result;
   2426 }
   2427 
   2428 template <typename CharT>
   2429 mozilla::Result<ISODate, ParserError>
   2430 TemporalParser<CharT>::dateSpecMonthDay() {
   2431  // DateSpecMonthDay :::
   2432  //   --? DateMonth DateSeparator[+Extended] DateDay
   2433  //   --? DateMonth DateSeparator[~Extended] DateDay
   2434 
   2435  ISODate result{};
   2436 
   2437  // Optional: --
   2438  string("--");
   2439 
   2440  result.year = AbsentYear;
   2441 
   2442  result.month = MOZ_TRY(dateMonth());
   2443 
   2444  // Optional |DateSeparator|.
   2445  dateSeparator();
   2446 
   2447  result.day = MOZ_TRY(dateDay());
   2448 
   2449  return result;
   2450 }
   2451 
   2452 template <typename CharT>
   2453 mozilla::Result<ZonedDateTimeString, ParserError>
   2454 TemporalParser<CharT>::parseTemporalCalendarString() {
   2455  MOZ_TRY(nonempty());
   2456 
   2457  // Handle the common case of a standalone calendar name first.
   2458  //
   2459  // All valid calendar names start with two alphabetic characters and none of
   2460  // the ParseISODateTime parse goals can start with two alphabetic characters.
   2461  // TemporalTimeString can start with 'T', so we can't only check the first
   2462  // character.
   2463  if (hasTwoAsciiAlpha()) {
   2464    ZonedDateTimeString result{};
   2465 
   2466    result.calendar = MOZ_TRY(parse(annotationValue()));
   2467 
   2468    return result;
   2469  }
   2470 
   2471  LikelyError likelyError{};
   2472 
   2473  // Try all five parse goals from ParseISODateTime in order.
   2474  //
   2475  // TemporalDateTimeString
   2476  // TemporalInstantString
   2477  // TemporalTimeString
   2478  // TemporalMonthDayString
   2479  // TemporalYearMonthString
   2480 
   2481  auto dateTime = parseTemporalDateTimeString();
   2482  if (dateTime.isOk()) {
   2483    return dateTime;
   2484  }
   2485  likelyError.update(dateTime, reader_.index());
   2486 
   2487  // Restart parsing from the start of the string.
   2488  reader_.reset();
   2489 
   2490  auto instant = parseTemporalInstantString();
   2491  if (instant.isOk()) {
   2492    return instant;
   2493  }
   2494  likelyError.update(instant, reader_.index());
   2495 
   2496  // Restart parsing from the start of the string.
   2497  reader_.reset();
   2498 
   2499  auto time = parseTemporalTimeString();
   2500  if (time.isOk()) {
   2501    return time;
   2502  }
   2503  likelyError.update(time, reader_.index());
   2504 
   2505  // Restart parsing from the start of the string.
   2506  reader_.reset();
   2507 
   2508  auto monthDay = parseTemporalMonthDayString();
   2509  if (monthDay.isOk()) {
   2510    return monthDay;
   2511  }
   2512  likelyError.update(monthDay, reader_.index());
   2513 
   2514  // Restart parsing from the start of the string.
   2515  reader_.reset();
   2516 
   2517  auto yearMonth = parseTemporalYearMonthString();
   2518  if (yearMonth.isOk()) {
   2519    return yearMonth;
   2520  }
   2521  likelyError.update(yearMonth, reader_.index());
   2522 
   2523  return likelyError.propagate();
   2524 }
   2525 
   2526 /**
   2527 * ParseTemporalCalendarString ( isoString )
   2528 */
   2529 template <typename CharT>
   2530 static auto ParseTemporalCalendarString(mozilla::Span<const CharT> str) {
   2531  TemporalParser<CharT> parser(str);
   2532  return parser.parseTemporalCalendarString();
   2533 }
   2534 
   2535 /**
   2536 * ParseTemporalCalendarString ( isoString )
   2537 */
   2538 static auto ParseTemporalCalendarString(Handle<JSLinearString*> str) {
   2539  JS::AutoCheckCannotGC nogc;
   2540  if (str->hasLatin1Chars()) {
   2541    return ParseTemporalCalendarString<Latin1Char>(str->latin1Range(nogc));
   2542  }
   2543  return ParseTemporalCalendarString<char16_t>(str->twoByteRange(nogc));
   2544 }
   2545 
   2546 /**
   2547 * ParseTemporalCalendarString ( isoString )
   2548 */
   2549 JSLinearString* js::temporal::ParseTemporalCalendarString(
   2550    JSContext* cx, Handle<JSString*> str) {
   2551  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2552  if (!linear) {
   2553    return nullptr;
   2554  }
   2555 
   2556  // Steps 1 and 3.a.
   2557  auto parseResult = ::ParseTemporalCalendarString(linear);
   2558  if (parseResult.isErr()) {
   2559    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2560                              parseResult.unwrapErr(), "calendar");
   2561    return nullptr;
   2562  }
   2563  ZonedDateTimeString parsed = parseResult.unwrap();
   2564 
   2565  ISODateTime unused;
   2566  if (!ParseISODateTime(cx, parsed, &unused)) {
   2567    return nullptr;
   2568  }
   2569 
   2570  // Step 2.b.
   2571  if (!parsed.calendar.present()) {
   2572    return cx->names().iso8601;
   2573  }
   2574 
   2575  // Steps 2.c and 3.c
   2576  return ToString(cx, linear, parsed.calendar);
   2577 }
   2578 
   2579 template <typename CharT>
   2580 mozilla::Result<ZonedDateTimeString, ParserError>
   2581 TemporalParser<CharT>::parseTemporalTimeString() {
   2582  MOZ_TRY(nonempty());
   2583 
   2584  // TemporalTimeString :::
   2585  //   AnnotatedTime
   2586  //   AnnotatedDateTime[~Zoned, +TimeRequired]
   2587 
   2588  LikelyError likelyError{};
   2589 
   2590  auto time = parse(annotatedTime());
   2591  if (time.isOk()) {
   2592    return time;
   2593  }
   2594  likelyError.update(time, reader_.index());
   2595 
   2596  // Reset and try the next option.
   2597  reader_.reset();
   2598 
   2599  auto dateTime = parse(annotatedDateTimeTimeRequired());
   2600  if (dateTime.isOk()) {
   2601    return dateTime;
   2602  }
   2603  likelyError.update(time, reader_.index());
   2604 
   2605  // Set current index to the likely error index to give better error messages
   2606  // when called from parserTemporal{Calendar,TimeZone}String.
   2607  reader_.reset(likelyError.index());
   2608 
   2609  return likelyError.propagate();
   2610 }
   2611 
   2612 /**
   2613 * ParseTemporalTimeString ( isoString )
   2614 */
   2615 template <typename CharT>
   2616 static auto ParseTemporalTimeString(mozilla::Span<const CharT> str) {
   2617  TemporalParser<CharT> parser(str);
   2618  return parser.parseTemporalTimeString();
   2619 }
   2620 
   2621 /**
   2622 * ParseTemporalTimeString ( isoString )
   2623 */
   2624 static auto ParseTemporalTimeString(Handle<JSLinearString*> str) {
   2625  JS::AutoCheckCannotGC nogc;
   2626  if (str->hasLatin1Chars()) {
   2627    return ParseTemporalTimeString<Latin1Char>(str->latin1Range(nogc));
   2628  }
   2629  return ParseTemporalTimeString<char16_t>(str->twoByteRange(nogc));
   2630 }
   2631 
   2632 /**
   2633 * ParseTemporalTimeString ( isoString )
   2634 */
   2635 bool js::temporal::ParseTemporalTimeString(JSContext* cx, Handle<JSString*> str,
   2636                                           Time* result) {
   2637  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2638  if (!linear) {
   2639    return false;
   2640  }
   2641 
   2642  // Steps 1-2.
   2643  auto parseResult = ::ParseTemporalTimeString(linear);
   2644  if (parseResult.isErr()) {
   2645    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2646                              parseResult.unwrapErr(), "time");
   2647    return false;
   2648  }
   2649  ZonedDateTimeString parsed = parseResult.unwrap();
   2650 
   2651  // Step 3.
   2652  ISODateTime dateTime;
   2653  if (!ParseISODateTime(cx, parsed, &dateTime)) {
   2654    return false;
   2655  }
   2656  *result = dateTime.time;
   2657 
   2658  // Step 4.
   2659  MOZ_ASSERT(!parsed.startOfDay);
   2660 
   2661  // Step 5.
   2662  return true;
   2663 }
   2664 
   2665 template <typename CharT>
   2666 mozilla::Result<ZonedDateTimeString, ParserError>
   2667 TemporalParser<CharT>::parseTemporalMonthDayString() {
   2668  MOZ_TRY(nonempty());
   2669 
   2670  // TemporalMonthDayString :::
   2671  //   AnnotatedMonthDay
   2672  //   AnnotatedDateTime[~Zoned, ~TimeRequired]
   2673 
   2674  LikelyError likelyError{};
   2675 
   2676  auto monthDay = parse(annotatedMonthDay());
   2677  if (monthDay.isOk()) {
   2678    auto result = monthDay.unwrap();
   2679 
   2680    // ParseISODateTime, step 3.
   2681    if (result.calendar.present() &&
   2682        !IsISO8601Calendar(reader_.substring(result.calendar))) {
   2683      return mozilla::Err(JSMSG_TEMPORAL_PARSER_MONTH_DAY_CALENDAR_NOT_ISO8601);
   2684    }
   2685    return result;
   2686  }
   2687  likelyError.update(monthDay, reader_.index());
   2688 
   2689  // Reset and try the next option.
   2690  reader_.reset();
   2691 
   2692  auto dateTime = parse(annotatedDateTime());
   2693  if (dateTime.isOk()) {
   2694    return dateTime;
   2695  }
   2696  likelyError.update(dateTime, reader_.index());
   2697 
   2698  // Set current index to the likely error index to give better error messages
   2699  // when called from parserTemporal{Calendar,TimeZone}String.
   2700  reader_.reset(likelyError.index());
   2701 
   2702  return likelyError.propagate();
   2703 }
   2704 
   2705 /**
   2706 * ParseTemporalMonthDayString ( isoString )
   2707 */
   2708 template <typename CharT>
   2709 static auto ParseTemporalMonthDayString(mozilla::Span<const CharT> str) {
   2710  TemporalParser<CharT> parser(str);
   2711  return parser.parseTemporalMonthDayString();
   2712 }
   2713 
   2714 /**
   2715 * ParseTemporalMonthDayString ( isoString )
   2716 */
   2717 static auto ParseTemporalMonthDayString(Handle<JSLinearString*> str) {
   2718  JS::AutoCheckCannotGC nogc;
   2719  if (str->hasLatin1Chars()) {
   2720    return ParseTemporalMonthDayString<Latin1Char>(str->latin1Range(nogc));
   2721  }
   2722  return ParseTemporalMonthDayString<char16_t>(str->twoByteRange(nogc));
   2723 }
   2724 
   2725 /**
   2726 * ParseTemporalMonthDayString ( isoString )
   2727 */
   2728 bool js::temporal::ParseTemporalMonthDayString(
   2729    JSContext* cx, Handle<JSString*> str, ISODate* result, bool* hasYear,
   2730    MutableHandle<JSString*> calendar) {
   2731  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2732  if (!linear) {
   2733    return false;
   2734  }
   2735 
   2736  // Steps 1-2.
   2737  auto parseResult = ::ParseTemporalMonthDayString(linear);
   2738  if (parseResult.isErr()) {
   2739    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2740                              parseResult.unwrapErr(), "month-day");
   2741    return false;
   2742  }
   2743  ZonedDateTimeString parsed = parseResult.unwrap();
   2744 
   2745  // Step 3.
   2746  ISODateTime dateTime;
   2747  if (!ParseISODateTime(cx, parsed, &dateTime)) {
   2748    return false;
   2749  }
   2750  *result = dateTime.date;
   2751 
   2752  // Steps 4-5.
   2753  *hasYear = parsed.date.year != AbsentYear;
   2754 
   2755  if (parsed.calendar.present()) {
   2756    calendar.set(ToString(cx, linear, parsed.calendar));
   2757    if (!calendar) {
   2758      return false;
   2759    }
   2760  }
   2761 
   2762  // Step 6.
   2763  return true;
   2764 }
   2765 
   2766 template <typename CharT>
   2767 mozilla::Result<ZonedDateTimeString, ParserError>
   2768 TemporalParser<CharT>::parseTemporalYearMonthString() {
   2769  MOZ_TRY(nonempty());
   2770 
   2771  // TemporalYearMonthString :::
   2772  //   AnnotatedYearMonth
   2773  //   AnnotatedDateTime[~Zoned, ~TimeRequired]
   2774 
   2775  LikelyError likelyError{};
   2776 
   2777  auto yearMonth = parse(annotatedYearMonth());
   2778  if (yearMonth.isOk()) {
   2779    auto result = yearMonth.unwrap();
   2780 
   2781    // ParseISODateTime, step 3.
   2782    if (result.calendar.present() &&
   2783        !IsISO8601Calendar(reader_.substring(result.calendar))) {
   2784      return mozilla::Err(
   2785          JSMSG_TEMPORAL_PARSER_YEAR_MONTH_CALENDAR_NOT_ISO8601);
   2786    }
   2787    return result;
   2788  }
   2789  likelyError.update(yearMonth, reader_.index());
   2790 
   2791  // Reset and try the next option.
   2792  reader_.reset();
   2793 
   2794  auto dateTime = parse(annotatedDateTime());
   2795  if (dateTime.isOk()) {
   2796    return dateTime;
   2797  }
   2798  likelyError.update(dateTime, reader_.index());
   2799 
   2800  // Set current index to the likely error index to give better error messages
   2801  // when called from parserTemporal{Calendar,TimeZone}String.
   2802  reader_.reset(likelyError.index());
   2803 
   2804  return likelyError.propagate();
   2805 }
   2806 
   2807 /**
   2808 * ParseTemporalYearMonthString ( isoString )
   2809 */
   2810 template <typename CharT>
   2811 static auto ParseTemporalYearMonthString(mozilla::Span<const CharT> str) {
   2812  TemporalParser<CharT> parser(str);
   2813  return parser.parseTemporalYearMonthString();
   2814 }
   2815 
   2816 /**
   2817 * ParseTemporalYearMonthString ( isoString )
   2818 */
   2819 static auto ParseTemporalYearMonthString(Handle<JSLinearString*> str) {
   2820  JS::AutoCheckCannotGC nogc;
   2821  if (str->hasLatin1Chars()) {
   2822    return ParseTemporalYearMonthString<Latin1Char>(str->latin1Range(nogc));
   2823  }
   2824  return ParseTemporalYearMonthString<char16_t>(str->twoByteRange(nogc));
   2825 }
   2826 
   2827 /**
   2828 * ParseTemporalYearMonthString ( isoString )
   2829 */
   2830 bool js::temporal::ParseTemporalYearMonthString(
   2831    JSContext* cx, Handle<JSString*> str, ISODate* result,
   2832    MutableHandle<JSString*> calendar) {
   2833  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2834  if (!linear) {
   2835    return false;
   2836  }
   2837 
   2838  // Steps 1-2.
   2839  auto parseResult = ::ParseTemporalYearMonthString(linear);
   2840  if (parseResult.isErr()) {
   2841    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2842                              parseResult.unwrapErr(), "year-month");
   2843    return false;
   2844  }
   2845  ZonedDateTimeString parsed = parseResult.unwrap();
   2846 
   2847  // Step 3.
   2848  ISODateTime dateTime;
   2849  if (!ParseISODateTime(cx, parsed, &dateTime)) {
   2850    return false;
   2851  }
   2852  *result = dateTime.date;
   2853 
   2854  if (parsed.calendar.present()) {
   2855    calendar.set(ToString(cx, linear, parsed.calendar));
   2856    if (!calendar) {
   2857      return false;
   2858    }
   2859  }
   2860 
   2861  return true;
   2862 }
   2863 
   2864 template <typename CharT>
   2865 mozilla::Result<ZonedDateTimeString, ParserError>
   2866 TemporalParser<CharT>::parseTemporalDateTimeString() {
   2867  MOZ_TRY(nonempty());
   2868 
   2869  // TemporalDateTimeString[Zoned] :::
   2870  //   AnnotatedDateTime[?Zoned, ~TimeRequired]
   2871 
   2872  return parse(annotatedDateTime());
   2873 }
   2874 
   2875 /**
   2876 * ParseTemporalDateTimeString ( isoString )
   2877 */
   2878 template <typename CharT>
   2879 static auto ParseTemporalDateTimeString(mozilla::Span<const CharT> str) {
   2880  TemporalParser<CharT> parser(str);
   2881  return parser.parseTemporalDateTimeString();
   2882 }
   2883 
   2884 /**
   2885 * ParseTemporalDateTimeString ( isoString )
   2886 */
   2887 static auto ParseTemporalDateTimeString(Handle<JSLinearString*> str) {
   2888  JS::AutoCheckCannotGC nogc;
   2889  if (str->hasLatin1Chars()) {
   2890    return ParseTemporalDateTimeString<Latin1Char>(str->latin1Range(nogc));
   2891  }
   2892  return ParseTemporalDateTimeString<char16_t>(str->twoByteRange(nogc));
   2893 }
   2894 
   2895 /**
   2896 * ParseTemporalDateTimeString ( isoString )
   2897 */
   2898 bool js::temporal::ParseTemporalDateTimeString(
   2899    JSContext* cx, Handle<JSString*> str, ISODateTime* result,
   2900    MutableHandle<JSString*> calendar) {
   2901  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2902  if (!linear) {
   2903    return false;
   2904  }
   2905 
   2906  // Steps 1-2.
   2907  auto parseResult = ::ParseTemporalDateTimeString(linear);
   2908  if (parseResult.isErr()) {
   2909    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2910                              parseResult.unwrapErr(), "date-time");
   2911    return false;
   2912  }
   2913  ZonedDateTimeString parsed = parseResult.unwrap();
   2914 
   2915  // Step 3.
   2916  if (!ParseISODateTime(cx, parsed, result)) {
   2917    return false;
   2918  }
   2919 
   2920  if (parsed.calendar.present()) {
   2921    calendar.set(ToString(cx, linear, parsed.calendar));
   2922    if (!calendar) {
   2923      return false;
   2924    }
   2925  }
   2926 
   2927  return true;
   2928 }
   2929 
   2930 template <typename CharT>
   2931 mozilla::Result<ZonedDateTimeString, ParserError>
   2932 TemporalParser<CharT>::parseTemporalZonedDateTimeString() {
   2933  MOZ_TRY(nonempty());
   2934 
   2935  // Parse goal: TemporalDateTimeString[+Zoned]
   2936  //
   2937  // TemporalDateTimeString[Zoned] :::
   2938  //   AnnotatedDateTime[?Zoned, ~TimeRequired]
   2939  //
   2940  // AnnotatedDateTime[Zoned, TimeRequired] :::
   2941  //   [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations?
   2942  //   [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations?
   2943 
   2944  ZonedDateTimeString result{};
   2945 
   2946  result = MOZ_TRY(dateTime(/* allowZ = */ true));
   2947 
   2948  result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   2949 
   2950  if (hasAnnotationStart()) {
   2951    result.calendar = MOZ_TRY(annotations());
   2952  }
   2953 
   2954  return complete(result);
   2955 }
   2956 
   2957 /**
   2958 * ParseTemporalZonedDateTimeString ( isoString )
   2959 */
   2960 template <typename CharT>
   2961 static auto ParseTemporalZonedDateTimeString(mozilla::Span<const CharT> str) {
   2962  TemporalParser<CharT> parser(str);
   2963  return parser.parseTemporalZonedDateTimeString();
   2964 }
   2965 
   2966 /**
   2967 * ParseTemporalZonedDateTimeString ( isoString )
   2968 */
   2969 static auto ParseTemporalZonedDateTimeString(Handle<JSLinearString*> str) {
   2970  JS::AutoCheckCannotGC nogc;
   2971  if (str->hasLatin1Chars()) {
   2972    return ParseTemporalZonedDateTimeString<Latin1Char>(str->latin1Range(nogc));
   2973  }
   2974  return ParseTemporalZonedDateTimeString<char16_t>(str->twoByteRange(nogc));
   2975 }
   2976 
   2977 /**
   2978 * ParseTemporalZonedDateTimeString ( isoString )
   2979 */
   2980 bool js::temporal::ParseTemporalZonedDateTimeString(
   2981    JSContext* cx, Handle<JSString*> str,
   2982    JS::MutableHandle<ParsedZonedDateTime> result) {
   2983  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   2984  if (!linear) {
   2985    return false;
   2986  }
   2987 
   2988  // Step 1.
   2989  auto parseResult = ::ParseTemporalZonedDateTimeString(linear);
   2990  if (parseResult.isErr()) {
   2991    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2992                              parseResult.unwrapErr(), "zoned date-time");
   2993    return false;
   2994  }
   2995  ZonedDateTimeString parsed = parseResult.unwrap();
   2996 
   2997  // Step 2. (ParseISODateTime, steps 2-3.)
   2998  Rooted<JSLinearString*> calendar(cx);
   2999  if (parsed.calendar.present()) {
   3000    calendar = ToString(cx, linear, parsed.calendar);
   3001    if (!calendar) {
   3002      return false;
   3003    }
   3004  }
   3005 
   3006  // Step 2. (ParseISODateTime, steps 4-21.)
   3007  ISODateTime dateTime;
   3008  if (!ParseISODateTime(cx, parsed, &dateTime)) {
   3009    return false;
   3010  }
   3011 
   3012  // Step 2. (ParseISODateTime, steps 22-23.)
   3013  bool isStartOfDay = parsed.startOfDay;
   3014 
   3015  // Step 2. (ParseISODateTime, steps 24-27.)
   3016  Rooted<ParsedTimeZone> timeZoneAnnotation(cx);
   3017  mozilla::MaybeOneOf<UTCTimeZone, OffsetTimeZone> timeZone;
   3018  {
   3019    MOZ_ASSERT(parsed.timeZone.hasAnnotation());
   3020 
   3021    // Case 1: 19700101T00:00Z[+02:00]
   3022    // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
   3023    //
   3024    // Case 2: 19700101T00:00+02:00[+02:00]
   3025    // { [[Z]]: false, [[OffsetString]]: "+02:00", [[Name]]: "+02:00" }
   3026    //
   3027    // Case 3: 19700101[+02:00]
   3028    // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
   3029    //
   3030    // Case 4: 19700101T00:00Z[Europe/Berlin]
   3031    // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
   3032    //
   3033    // Case 5: 19700101T00:00+01:00[Europe/Berlin]
   3034    // { [[Z]]: false, [[OffsetString]]: "+01:00", [[Name]]: "Europe/Berlin" }
   3035    //
   3036    // Case 6: 19700101[Europe/Berlin]
   3037    // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
   3038 
   3039    const auto& annotation = parsed.timeZone.annotation;
   3040    if (!ParseTimeZoneAnnotation(cx, annotation, linear, &timeZoneAnnotation)) {
   3041      return false;
   3042    }
   3043 
   3044    if (parsed.timeZone.isUTC()) {
   3045      timeZone.construct<UTCTimeZone>();
   3046    } else if (parsed.timeZone.hasOffset()) {
   3047      timeZone.construct<OffsetTimeZone>(
   3048          ParseDateTimeUTCOffset(parsed.timeZone.offset));
   3049    }
   3050  }
   3051 
   3052  // Step 2. (ParseISODateTime, step 28.)
   3053  result.set(ParsedZonedDateTime{
   3054      dateTime,
   3055      calendar,
   3056      timeZoneAnnotation.get(),
   3057      std::move(timeZone),
   3058      isStartOfDay,
   3059  });
   3060  return true;
   3061 }
   3062 
   3063 template <typename CharT>
   3064 mozilla::Result<ZonedDateTimeString, ParserError>
   3065 TemporalParser<CharT>::parseTemporalRelativeToString() {
   3066  MOZ_TRY(nonempty());
   3067 
   3068  // Parse goals:
   3069  // TemporalDateTimeString[+Zoned] and TemporalDateTimeString[~Zoned]
   3070  //
   3071  // TemporalDateTimeString[Zoned] :::
   3072  //   AnnotatedDateTime[?Zoned, ~TimeRequired]
   3073  //
   3074  // AnnotatedDateTime[Zoned, TimeRequired] :::
   3075  //   [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations?
   3076  //   [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations?
   3077 
   3078  ZonedDateTimeString result{};
   3079 
   3080  result = MOZ_TRY(dateTime(/* allowZ = */ true));
   3081 
   3082  if (hasTimeZoneAnnotationStart()) {
   3083    result.timeZone.annotation = MOZ_TRY(timeZoneAnnotation());
   3084  }
   3085 
   3086  if (hasAnnotationStart()) {
   3087    result.calendar = MOZ_TRY(annotations());
   3088  }
   3089 
   3090  return complete(result);
   3091 }
   3092 
   3093 /**
   3094 * ParseTemporalRelativeToString ( isoString )
   3095 */
   3096 template <typename CharT>
   3097 static auto ParseTemporalRelativeToString(mozilla::Span<const CharT> str) {
   3098  TemporalParser<CharT> parser(str);
   3099  return parser.parseTemporalRelativeToString();
   3100 }
   3101 
   3102 /**
   3103 * ParseTemporalRelativeToString ( isoString )
   3104 */
   3105 static auto ParseTemporalRelativeToString(Handle<JSLinearString*> str) {
   3106  JS::AutoCheckCannotGC nogc;
   3107  if (str->hasLatin1Chars()) {
   3108    return ParseTemporalRelativeToString<Latin1Char>(str->latin1Range(nogc));
   3109  }
   3110  return ParseTemporalRelativeToString<char16_t>(str->twoByteRange(nogc));
   3111 }
   3112 
   3113 /**
   3114 * ParseTemporalRelativeToString ( isoString )
   3115 */
   3116 bool js::temporal::ParseTemporalRelativeToString(
   3117    JSContext* cx, Handle<JSString*> str,
   3118    MutableHandle<ParsedZonedDateTime> result) {
   3119  Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   3120  if (!linear) {
   3121    return false;
   3122  }
   3123 
   3124  // Steps 1-2.
   3125  auto parseResult = ::ParseTemporalRelativeToString(linear);
   3126  if (parseResult.isErr()) {
   3127    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3128                              parseResult.unwrapErr(), "relative date-time");
   3129    return false;
   3130  }
   3131  ZonedDateTimeString parsed = parseResult.unwrap();
   3132 
   3133  // Step 3.
   3134  if (parsed.timeZone.isUTC() && !parsed.timeZone.hasAnnotation()) {
   3135    JS_ReportErrorNumberASCII(
   3136        cx, GetErrorMessage, nullptr,
   3137        JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR_WITHOUT_NAME,
   3138        "relative date-time");
   3139    return false;
   3140  }
   3141 
   3142  // Step 4. (ParseISODateTime, steps 1-18.)
   3143  ISODateTime dateTime;
   3144  if (!ParseISODateTime(cx, parsed, &dateTime)) {
   3145    return false;
   3146  }
   3147  bool isStartOfDay = parsed.startOfDay;
   3148 
   3149  // Step 4. (ParseISODateTime, steps 19-22.)
   3150  Rooted<ParsedTimeZone> timeZoneAnnotation(cx);
   3151  mozilla::MaybeOneOf<UTCTimeZone, OffsetTimeZone> timeZone;
   3152  if (parsed.timeZone.hasAnnotation()) {
   3153    // Case 1: 19700101Z[+02:00]
   3154    // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
   3155    //
   3156    // Case 2: 19700101+00:00[+02:00]
   3157    // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
   3158    //
   3159    // Case 3: 19700101[+02:00]
   3160    // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
   3161    //
   3162    // Case 4: 19700101Z[Europe/Berlin]
   3163    // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
   3164    //
   3165    // Case 5: 19700101+00:00[Europe/Berlin]
   3166    // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
   3167    //
   3168    // Case 6: 19700101[Europe/Berlin]
   3169    // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
   3170 
   3171    const auto& annotation = parsed.timeZone.annotation;
   3172    if (!ParseTimeZoneAnnotation(cx, annotation, linear, &timeZoneAnnotation)) {
   3173      return false;
   3174    }
   3175 
   3176    if (parsed.timeZone.isUTC()) {
   3177      timeZone.construct<UTCTimeZone>();
   3178    } else if (parsed.timeZone.hasOffset()) {
   3179      timeZone.construct<OffsetTimeZone>(
   3180          ParseDateTimeUTCOffset(parsed.timeZone.offset));
   3181    }
   3182  } else {
   3183    // GetTemporalRelativeToOption ignores any other time zone information when
   3184    // no bracketed time zone annotation is present.
   3185    timeZoneAnnotation.set(ParsedTimeZone{});
   3186  }
   3187 
   3188  // Step 4. (ParseISODateTime, steps 23-24.)
   3189  JSLinearString* calendar = nullptr;
   3190  if (parsed.calendar.present()) {
   3191    calendar = ToString(cx, linear, parsed.calendar);
   3192    if (!calendar) {
   3193      return false;
   3194    }
   3195  }
   3196 
   3197  // Step 4. (Return)
   3198  result.set(ParsedZonedDateTime{
   3199      dateTime,
   3200      calendar,
   3201      timeZoneAnnotation.get(),
   3202      std::move(timeZone),
   3203      isStartOfDay,
   3204  });
   3205  return true;
   3206 }
   3207 
   3208 void js::temporal::ParsedTimeZone::trace(JSTracer* trc) {
   3209  TraceNullableRoot(trc, &name, "ParsedTimeZone::name");
   3210 }
   3211 
   3212 void js::temporal::ParsedZonedDateTime::trace(JSTracer* trc) {
   3213  TraceNullableRoot(trc, &calendar, "ParsedZonedDateTime::calendar");
   3214  timeZoneAnnotation.trace(trc);
   3215 }