%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/temporal/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/temporal/temporal-parser.cc |
// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/temporal/temporal-parser.h" #include "src/base/bounds.h" #include "src/base/optional.h" #include "src/objects/string-inl.h" #include "src/strings/char-predicates-inl.h" namespace v8 { namespace internal { namespace { // Temporal #prod-TZLeadingChar inline constexpr bool IsTZLeadingChar(base::uc32 c) { return base::IsInRange(AsciiAlphaToLower(c), 'a', 'z') || c == '.' || c == '_'; } // Temporal #prod-TZChar inline constexpr bool IsTZChar(base::uc32 c) { return IsTZLeadingChar(c) || c == '-'; } // Temporal #prod-DecimalSeparator inline constexpr bool IsDecimalSeparator(base::uc32 c) { return c == '.' || c == ','; } // Temporal #prod-DateTimeSeparator inline constexpr bool IsDateTimeSeparator(base::uc32 c) { return c == ' ' || AsciiAlphaToLower(c) == 't'; } // Temporal #prod-ASCIISign inline constexpr bool IsAsciiSign(base::uc32 c) { return c == '-' || c == '+'; } // Temporal #prod-Sign inline constexpr bool IsSign(base::uc32 c) { return c == 0x2212 || IsAsciiSign(c); } // Temporal #prod-TimeZoneUTCOffsetSign inline constexpr bool IsTimeZoneUTCOffsetSign(base::uc32 c) { return IsSign(c); } inline constexpr base::uc32 CanonicalSign(base::uc32 c) { return c == 0x2212 ? '-' : c; } inline constexpr int32_t ToInt(base::uc32 c) { return c - '0'; } // A helper template to make the scanning of production w/ two digits simpler. template <typename Char> bool HasTwoDigits(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() >= (s + 2) && IsDecimalDigit(str[s]) && IsDecimalDigit(str[s + 1])) { *out = ToInt(str[s]) * 10 + ToInt(str[s + 1]); return true; } return false; } // A helper template to make the scanning of production w/ a single two digits // value simpler. template <typename Char> int32_t ScanTwoDigitsExpectValue(base::Vector<Char> str, int32_t s, int32_t expected, int32_t* out) { return HasTwoDigits<Char>(str, s, out) && (*out == expected) ? 2 : 0; } // A helper template to make the scanning of production w/ two digits value in a // range simpler. template <typename Char> int32_t ScanTwoDigitsExpectRange(base::Vector<Char> str, int32_t s, int32_t min, int32_t max, int32_t* out) { return HasTwoDigits<Char>(str, s, out) && base::IsInRange(*out, min, max) ? 2 : 0; } // A helper template to make the scanning of production w/ two digits value as 0 // or in a range simpler. template <typename Char> int32_t ScanTwoDigitsExpectZeroOrRange(base::Vector<Char> str, int32_t s, int32_t min, int32_t max, int32_t* out) { return HasTwoDigits<Char>(str, s, out) && (*out == 0 || base::IsInRange(*out, min, max)) ? 2 : 0; } /** * The TemporalParser use two types of internal routine: * - Scan routines: Follow the function signature below: * template <typename Char> int32_t Scan$ProductionName( * base::Vector<Char> str, int32_t s, R* out) * * These routine scan the next item from position s in str and store the * parsed result into out if the expected string is successfully scanned. * It return the length of matched text from s or 0 to indicate no * expected item matched. * * - Satisfy routines: Follow the function sigature below: * template <typename Char> * bool Satisfy$ProductionName(base::Vector<Char> str, R* r); * It scan from the beginning of the str by calling Scan routines to put * parsed result into r and return true if the entire str satisfy the * production. It internally use Scan routines. * * TODO(ftang) investigate refactoring to class before shipping * Reference to RegExpParserImpl by encapsulating the cursor position and * only manipulating the current character and position with Next(), * Advance(), current(), etc */ // For Hour Production // Hour: // [0 1] Digit // 2 [0 1 2 3] template <typename Char> int32_t ScanHour(base::Vector<Char> str, int32_t s, int32_t* out) { return ScanTwoDigitsExpectRange<Char>(str, s, 0, 23, out); } // UnpaddedHour : // DecimalDigit // 1 DecimalDigit // 20 // 21 // 22 // 23 template <typename Char> int32_t ScanUnpaddedHour(base::Vector<Char> str, int32_t s) { int32_t dummy; int32_t len = ScanTwoDigitsExpectRange<Char>(str, s, 10, 23, &dummy); if (len > 0) return len; if (str.length() >= (s + 1) && IsDecimalDigit(str[s])) return 1; return 0; } // MinuteSecond: // [0 1 2 3 4 5] Digit template <typename Char> int32_t ScanMinuteSecond(base::Vector<Char> str, int32_t s, int32_t* out) { return ScanTwoDigitsExpectRange<Char>(str, s, 0, 59, out); } // For the forward production in the grammar such as // ProductionB: // ProductionT #define SCAN_FORWARD(B, T, R) \ template <typename Char> \ int32_t Scan##B(base::Vector<Char> str, int32_t s, R* r) { \ return Scan##T(str, s, r); \ } // Same as above but store the result into a particular field in R // For the forward production in the grammar such as // ProductionB: // ProductionT1 // ProductionT2 #define SCAN_EITHER_FORWARD(B, T1, T2, R) \ template <typename Char> \ int32_t Scan##B(base::Vector<Char> str, int32_t s, R* r) { \ int32_t len; \ if ((len = Scan##T1(str, s, r)) > 0) return len; \ return Scan##T2(str, s, r); \ } // TimeHour: Hour SCAN_FORWARD(TimeHour, Hour, int32_t) // TimeMinute: MinuteSecond SCAN_FORWARD(TimeMinute, MinuteSecond, int32_t) // TimeSecond: // MinuteSecond // 60 template <typename Char> int32_t ScanTimeSecond(base::Vector<Char> str, int32_t s, int32_t* out) { return ScanTwoDigitsExpectRange<Char>(str, s, 0, 60, out); } constexpr int kPowerOfTen[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; // FractionalPart : Digit{1,9} template <typename Char> int32_t ScanFractionalPart(base::Vector<Char> str, int32_t s, int32_t* out) { int32_t cur = s; if ((str.length() < (cur + 1)) || !IsDecimalDigit(str[cur])) return 0; *out = ToInt(str[cur++]); while ((cur < str.length()) && ((cur - s) < 9) && IsDecimalDigit(str[cur])) { *out = 10 * (*out) + ToInt(str[cur++]); } *out *= kPowerOfTen[9 - (cur - s)]; return cur - s; } // TimeFraction: FractionalPart SCAN_FORWARD(TimeFractionalPart, FractionalPart, int32_t) // Fraction: DecimalSeparator FractionalPart // DecimalSeparator: one of , . template <typename Char> int32_t ScanFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanFractionalPart(str, s + 1, out)) == 0) return 0; return len + 1; } // TimeFraction: DecimalSeparator TimeFractionalPart // DecimalSeparator: one of , . template <typename Char> int32_t ScanTimeFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanTimeFractionalPart(str, s + 1, out)) == 0) return 0; return len + 1; } template <typename Char> int32_t ScanTimeFraction(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { return ScanTimeFraction(str, s, &(r->time_nanosecond)); } // TimeSpec: // TimeHour // TimeHour : TimeMinute // TimeHour : TimeMinute : TimeSecond [TimeFraction] // TimeHour TimeMinute // TimeHour TimeMinute TimeSecond [TimeFraction] template <typename Char> int32_t ScanTimeSpec(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t time_hour, time_minute, time_second; int32_t len; int32_t cur = s; if ((len = ScanTimeHour(str, cur, &time_hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // TimeHour r->time_hour = time_hour; return cur - s; } if (str[cur] == ':') { cur++; if ((len = ScanTimeMinute(str, cur, &time_minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || (str[cur] != ':')) { // TimeHour : TimeMinute r->time_hour = time_hour; r->time_minute = time_minute; return cur - s; } cur++; if ((len = ScanTimeSecond(str, cur, &time_second)) == 0) return 0; } else { if ((len = ScanTimeMinute(str, cur, &time_minute)) == 0) { // TimeHour r->time_hour = time_hour; return cur - s; } cur += len; if ((len = ScanTimeSecond(str, cur, &time_second)) == 0) { // TimeHour TimeMinute r->time_hour = time_hour; r->time_minute = time_minute; return cur - s; } } cur += len; len = ScanTimeFraction(str, cur, r); r->time_hour = time_hour; r->time_minute = time_minute; r->time_second = time_second; cur += len; return cur - s; } // TimeSpecSeparator: DateTimeSeparator TimeSpec // DateTimeSeparator: SPACE, 't', or 'T' template <typename Char> int32_t ScanTimeSpecSeparator(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (!(((s + 1) < str.length()) && IsDateTimeSeparator(str[s]))) return 0; int32_t len = ScanTimeSpec(str, s + 1, r); return (len == 0) ? 0 : len + 1; } // DateExtendedYear: Sign Digit Digit Digit Digit Digit Digit template <typename Char> int32_t ScanDateExtendedYear(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 7)) return 0; if (IsSign(str[s]) && IsDecimalDigit(str[s + 1]) && IsDecimalDigit(str[s + 2]) && IsDecimalDigit(str[s + 3]) && IsDecimalDigit(str[s + 4]) && IsDecimalDigit(str[s + 5]) && IsDecimalDigit(str[s + 6])) { int32_t sign = (CanonicalSign(str[s]) == '-') ? -1 : 1; *out = sign * (ToInt(str[s + 1]) * 100000 + ToInt(str[s + 2]) * 10000 + ToInt(str[s + 3]) * 1000 + ToInt(str[s + 4]) * 100 + ToInt(str[s + 5]) * 10 + ToInt(str[s + 6])); // In the end of #sec-temporal-iso8601grammar // It is a Syntax Error if DateExtendedYear is "-000000" or "−000000" // (U+2212 MINUS SIGN followed by 000000). if (sign == -1 && *out == 0) return 0; return 7; } return 0; } // DateFourDigitYear: Digit Digit Digit Digit template <typename Char> int32_t ScanDateFourDigitYear(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 4)) return 0; if (IsDecimalDigit(str[s]) && IsDecimalDigit(str[s + 1]) && IsDecimalDigit(str[s + 2]) && IsDecimalDigit(str[s + 3])) { *out = ToInt(str[s]) * 1000 + ToInt(str[s + 1]) * 100 + ToInt(str[s + 2]) * 10 + ToInt(str[s + 3]); return 4; } return 0; } // DateYear: // DateFourDigitYear // DateExtendedYear // The lookahead is at most 1 char. SCAN_EITHER_FORWARD(DateYear, DateFourDigitYear, DateExtendedYear, int32_t) // DateMonth: // 0 NonzeroDigit // 10 // 11 // 12 template <typename Char> int32_t ScanDateMonth(base::Vector<Char> str, int32_t s, int32_t* out) { return ScanTwoDigitsExpectRange<Char>(str, s, 1, 12, out); } // DateDay: // 0 NonzeroDigit // 1 Digit // 2 Digit // 30 // 31 template <typename Char> int32_t ScanDateDay(base::Vector<Char> str, int32_t s, int32_t* out) { return ScanTwoDigitsExpectRange<Char>(str, s, 1, 31, out); } // Date: // DateYear - DateMonth - DateDay // DateYear DateMonth DateDay template <typename Char> int32_t ScanDate(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t date_year, date_month, date_day; int32_t cur = s; int32_t len; if ((len = ScanDateYear(str, cur, &date_year)) == 0) return 0; if (((cur += len) + 1) > str.length()) return 0; if (str[cur] == '-') { cur++; if ((len = ScanDateMonth(str, cur, &date_month)) == 0) return 0; cur += len; if (((cur + 1) > str.length()) || (str[cur++] != '-')) return 0; } else { if ((len = ScanDateMonth(str, cur, &date_month)) == 0) return 0; cur += len; } if ((len = ScanDateDay(str, cur, &date_day)) == 0) return 0; r->date_year = date_year; r->date_month = date_month; r->date_day = date_day; return cur + len - s; } // DateMonthWithThirtyOneDays : one of // 01 03 05 07 08 10 12 template <typename Char> int32_t ScanDateMonthWithThirtyOneDays(base::Vector<Char> str, int32_t s) { int32_t value; if (!HasTwoDigits(str, s, &value)) return false; return value == 1 || value == 3 || value == 5 || value == 7 || value == 8 || value == 10 || value == 12; } // TimeZoneUTCOffsetHour: Hour SCAN_FORWARD(TimeZoneUTCOffsetHour, Hour, int32_t) // TimeZoneUTCOffsetMinute SCAN_FORWARD(TimeZoneUTCOffsetMinute, MinuteSecond, int32_t) // TimeZoneUTCOffsetSecond SCAN_FORWARD(TimeZoneUTCOffsetSecond, MinuteSecond, int32_t) // TimeZoneUTCOffsetFractionalPart: FractionalPart // See PR1796 SCAN_FORWARD(TimeZoneUTCOffsetFractionalPart, FractionalPart, int32_t) // TimeZoneUTCOffsetFraction: DecimalSeparator TimeZoneUTCOffsetFractionalPart // See PR1796 template <typename Char> int32_t ScanTimeZoneUTCOffsetFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanTimeZoneUTCOffsetFractionalPart(str, s + 1, out)) > 0) { return len + 1; } return 0; } // TimeZoneNumericUTCOffset: // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : // TimeZoneUTCOffsetSecond [TimeZoneUTCOffsetFraction] TimeZoneUTCOffsetSign // TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond // [TimeZoneUTCOffsetFraction] template <typename Char> int32_t ScanTimeZoneNumericUTCOffset(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len, hour, minute, second, nanosecond; int32_t cur = s; if ((str.length() < (cur + 1)) || (!IsTimeZoneUTCOffsetSign(str[cur]))) { return 0; } int32_t sign = (CanonicalSign(str[cur++]) == '-') ? -1 : 1; if ((len = ScanTimeZoneUTCOffsetHour(str, cur, &hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour r->tzuo_sign = sign; r->tzuo_hour = hour; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } if (str[cur] == ':') { cur++; if ((len = ScanTimeZoneUTCOffsetMinute(str, cur, &minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || str[cur] != ':') { // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } cur++; if ((len = ScanTimeZoneUTCOffsetSecond(str, cur, &second)) == 0) return 0; } else { if ((len = ScanTimeZoneUTCOffsetMinute(str, cur, &minute)) == 0) { // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour r->tzuo_sign = sign; r->tzuo_hour = hour; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } cur += len; if ((len = ScanTimeZoneUTCOffsetSecond(str, cur, &second)) == 0) { // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } } cur += len; len = ScanTimeZoneUTCOffsetFraction(str, cur, &nanosecond); r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->tzuo_second = second; if (len > 0) r->tzuo_nanosecond = nanosecond; r->offset_string_start = s; r->offset_string_length = cur + len - s; cur += len; return cur - s; } // TimeZoneUTCOffset: // TimeZoneNumericUTCOffset // UTCDesignator template <typename Char> int32_t ScanTimeZoneUTCOffset(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 1)) return 0; if (AsciiAlphaToLower(str[s]) == 'z') { // UTCDesignator r->utc_designator = true; return 1; } // TimeZoneNumericUTCOffset return ScanTimeZoneNumericUTCOffset(str, s, r); } // TimeZoneIANANameComponent : // TZLeadingChar TZChar{0,13} but not one of . or .. template <typename Char> int32_t ScanTimeZoneIANANameComponent(base::Vector<Char> str, int32_t s) { int32_t cur = s; if (str.length() < (cur + 1) || !IsTZLeadingChar(str[cur++])) return 0; while (((cur) < str.length()) && ((cur - s) < 14) && IsTZChar(str[cur])) { cur++; } if ((cur - s) == 1 && str[s] == '.') return 0; if ((cur - s) == 2 && str[s] == '.' && str[s + 1] == '.') return 0; return cur - s; } // TimeZoneIANALegacyName : // Etc/GMT0 // GMT0 // GMT-0 // GMT+0 // EST5EDT // CST6CDT // MST7MDT // PST8PDT template <typename Char> int32_t ScanTimeZoneIANALegacyName(base::Vector<Char> str, int32_t s) { int32_t cur = s; { constexpr int32_t len = 4; if (str.length() < cur + len) return 0; if (CompareCharsEqual(str.begin() + cur, "GMT0", len)) return len; } { constexpr int32_t len = 5; if (str.length() < cur + len) return 0; if (CompareCharsEqual(str.begin() + cur, "GMT+0", len) || CompareCharsEqual(str.begin() + cur, "GMT-0", len)) { return len; } } { constexpr int32_t len = 7; if (str.length() < cur + len) return 0; if (CompareCharsEqual(str.begin() + cur, "EST5EDT", len) || CompareCharsEqual(str.begin() + cur, "CST6CDT", len) || CompareCharsEqual(str.begin() + cur, "MST7MDT", len) || CompareCharsEqual(str.begin() + cur, "PST8PDT", len)) { return len; } } { constexpr int32_t len = 8; if (str.length() < cur + len) return 0; if (CompareCharsEqual(str.begin() + cur, "Etc/GMT0", len)) return len; } return 0; } // Etc/GMT ASCIISign UnpaddedHour template <typename Char> int32_t ScanEtcGMTASCIISignUnpaddedHour(base::Vector<Char> str, int32_t s) { if ((s + 9) > str.length()) return 0; int32_t cur = s; int32_t len = arraysize("Etc/GMT") - 1; if (!CompareCharsEqual(str.begin() + cur, "Etc/GMT", len)) return 0; cur += len; Char sign = str[cur++]; if (!IsAsciiSign(sign)) return 0; len = ScanUnpaddedHour(str, cur); if (len == 0) return 0; cur += len; return cur - s; } // TimeZoneIANANameTail : // TimeZoneIANANameComponent // TimeZoneIANANameComponent / TimeZoneIANANameTail // TimeZoneIANAName : // Etc/GMT ASCIISign UnpaddedHour // TimeZoneIANANameTail // TimeZoneIANALegacyName // The spec text use tail recusion with TimeZoneIANANameComponent and // TimeZoneIANANameTail. In our implementation, we use an iteration loop // instead. template <typename Char> int32_t ScanTimeZoneIANAName(base::Vector<Char> str, int32_t s) { int32_t len; if ((len = ScanEtcGMTASCIISignUnpaddedHour(str, s)) > 0 || (len = ScanTimeZoneIANALegacyName(str, s)) > 0) { return len; } int32_t cur = s; if ((len = ScanTimeZoneIANANameComponent(str, cur)) == 0) return 0; cur += len; while ((str.length() > (cur + 1)) && (str[cur] == '/')) { cur++; if ((len = ScanTimeZoneIANANameComponent(str, cur)) == 0) { return 0; } // TimeZoneIANANameComponent / TimeZoneIANAName cur += len; } return cur - s; } // TimeZoneUTCOffsetName // Sign Hour // Sign Hour : MinuteSecond // Sign Hour MinuteSecond // Sign Hour : MinuteSecond : MinuteSecond [Fraction] // Sign Hour MinuteSecond MinuteSecond [Fraction] // template <typename Char> int32_t ScanTimeZoneUTCOffsetName(base::Vector<Char> str, int32_t s) { int32_t cur = s; int32_t len; if ((str.length() < (s + 3)) || !IsSign(str[cur++])) return 0; int32_t hour, minute, second, fraction; if ((len = ScanHour(str, cur, &hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // Sign Hour return cur - s; } if (str[cur] == ':') { // Sign Hour : cur++; if ((len = ScanMinuteSecond(str, cur, &minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || (str[cur] != ':')) { // Sign Hour : MinuteSecond return cur - s; } cur++; // Sign Hour : MinuteSecond : if ((len = ScanMinuteSecond(str, cur, &second)) == 0) return 0; cur += len; len = ScanFraction(str, cur, &fraction); return cur + len - s; } else { if ((len = ScanMinuteSecond(str, cur, &minute)) == 0) { // Sign Hour return cur - s; } cur += len; if ((len = ScanMinuteSecond(str, cur, &second)) == 0) { // Sign Hour MinuteSecond return cur - s; } cur += len; len = ScanFraction(str, cur, &fraction); // Sign Hour MinuteSecond MinuteSecond [Fraction] cur += len; return cur - s; } } // TimeZoneBracketedName // TimeZoneIANAName // "Etc/GMT" ASCIISign Hour // TimeZoneUTCOffsetName // Since "Etc/GMT" also fit TimeZoneIANAName so we need to try // "Etc/GMT" ASCIISign Hour first. template <typename Char> int32_t ScanEtcGMTAsciiSignHour(base::Vector<Char> str, int32_t s) { if ((s + 10) > str.length()) return 0; int32_t cur = s; if ((str[cur++] != 'E') || (str[cur++] != 't') || (str[cur++] != 'c') || (str[cur++] != '/') || (str[cur++] != 'G') || (str[cur++] != 'M') || (str[cur++] != 'T')) { return 0; } Char sign = str[cur++]; if (!IsAsciiSign(sign)) return 0; int32_t hour; int32_t len = ScanHour(str, cur, &hour); if (len == 0) return 0; // "Etc/GMT" ASCIISign Hour return 10; } template <typename Char> int32_t ScanTimeZoneIdentifier(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r); // TimeZoneBracketedAnnotation : // [ TimeZoneIdentifier ] template <typename Char> int32_t ScanTimeZoneBracketedAnnotation(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if ((str.length() < (s + 3)) || (str[s] != '[')) return 0; int32_t cur = s + 1; int32_t len = ScanTimeZoneIdentifier(str, cur, r); cur += len; if (len == 0 || str.length() < (cur + 1) || (str[cur] != ']')) { // Only ScanTimeZoneBracketedAnnotation know the post condition of // TimeZoneIdentifier is not matched so we need to reset here. r->tzi_name_start = 0; r->tzi_name_length = 0; return 0; } cur++; return cur - s; } // TimeZoneOffsetRequired: // TimeZoneUTCOffset [TimeZoneBracketedAnnotation] template <typename Char> int32_t ScanTimeZoneOffsetRequired(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanTimeZoneUTCOffset(str, cur, r); if (cur == s) return 0; cur += ScanTimeZoneBracketedAnnotation(str, cur, r); return cur - s; } // TimeZoneNameRequired: // [TimeZoneUTCOffset] TimeZoneBracketedAnnotation template <typename Char> int32_t ScanTimeZoneNameRequired(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanTimeZoneUTCOffset(str, cur, r); int32_t len = ScanTimeZoneBracketedAnnotation(str, cur, r); if (len == 0) return 0; cur += len; return cur - s; } // TimeZone: // TimeZoneUTCOffset [TimeZoneBracketedAnnotation] // TimeZoneBracketedAnnotation template <typename Char> int32_t ScanTimeZone(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len; // TimeZoneUTCOffset [TimeZoneBracketedAnnotation] if ((len = ScanTimeZoneUTCOffset(str, cur, r)) > 0) { cur += len; // [TimeZoneBracketedAnnotation] len = ScanTimeZoneBracketedAnnotation(str, cur, r); cur += len; return cur - s; } // TimeZoneBracketedAnnotation return ScanTimeZoneBracketedAnnotation(str, cur, r); } // ValidMonthDay : // DateMonth [-] 0 NonZeroDigit // DateMonth [-] 1 DecimalDigit // DateMonth [-] 2 DecimalDigit // DateMonth [-] 30 but not one of 0230 or 02-30 // DateMonthWithThirtyOneDays [-] 31 template <typename Char> int32_t ScanValidMonthDay(base::Vector<Char> str, int32_t s) { int32_t len; int32_t cur = s; int32_t date_month; if ((len = ScanDateMonth(str, cur, &date_month)) > 0) { cur += len; if (str.length() >= (cur + 1)) { if (str[cur] == '-') cur++; int32_t day_of_month; if ((len = ScanTwoDigitsExpectRange(str, cur, 1, 30, &day_of_month)) > 0) { cur += len; // 0 NonZeroDigit // 1 DecimalDigit // 2 DecimalDigit // 30 but not one of 0230 or 02-30 if (date_month != 2 || day_of_month != 30) { return cur - s; } } } } // Reset cur cur = s; // DateMonthWithThirtyOneDays [-] 31 if ((len = ScanDateMonthWithThirtyOneDays(str, cur)) > 0) { cur += len; if (str.length() >= (cur + 1)) { if (str[cur] == '-') cur++; int32_t dummy; if ((len = ScanTwoDigitsExpectValue(str, cur, 31, &dummy)) > 0) { cur += len; return cur - s; } } } return 0; } template <typename Char> int32_t ScanDateSpecYearMonth(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r); // TimeSpecWithOptionalTimeZoneNotAmbiguous : // TimeSpec [TimeZone] but not one of ValidMonthDay or DateSpecYearMonth template <typename Char> int32_t ScanTimeSpecWithOptionalTimeZoneNotAmbiguous(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len; if ((len = ScanTimeSpec(str, cur, r)) == 0) return 0; cur += len; // [TimeZone] len = ScanTimeZone(str, cur, r); cur += len; len = cur - s; // If it match ValidMonthDay, consider invalid. if (ScanValidMonthDay(str, s) == len) return 0; // If it match DateSpecYearMonth, consider invalid. ParsedISO8601Result tmp; if (ScanDateSpecYearMonth(str, s, &tmp) == len) return 0; return len; } // CalendarNameComponent: // CalChar {3,8} template <typename Char> int32_t ScanCalendarNameComponent(base::Vector<Char> str, int32_t s) { int32_t cur = s; while ((cur < str.length()) && IsAlphaNumeric(str[cur])) cur++; if ((cur - s) < 3 || (cur - s) > 8) return 0; return cur - s; } // CalendarNameTail : // CalendarNameComponent // CalendarNameComponent - CalendarNameTail // CalendarName : // CalendarNameTail // The spec text use tail recusion with CalendarNameComponent and // CalendarNameTail. In our implementation, we use an iteration loop instead. template <typename Char> int32_t ScanCalendarName(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len; if ((len = ScanCalendarNameComponent(str, cur)) == 0) return 0; cur += len; while ((str.length() > (cur + 1)) && (str[cur++] == '-')) { if ((len = ScanCalendarNameComponent(str, cur)) == 0) return 0; // CalendarNameComponent - CalendarName cur += len; } r->calendar_name_start = s; r->calendar_name_length = cur - s; return cur - s; } // Calendar: '[u-ca=' CalendarName ']' template <typename Char> int32_t ScanCalendar(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 7)) return 0; int32_t cur = s; // "[u-ca=" if ((str[cur++] != '[') || (str[cur++] != 'u') || (str[cur++] != '-') || (str[cur++] != 'c') || (str[cur++] != 'a') || (str[cur++] != '=')) { return 0; } int32_t len = ScanCalendarName(str, cur, r); if (len == 0) return 0; if ((str.length() < (cur + len + 1)) || (str[cur + len] != ']')) { // Only ScanCalendar know the post condition of CalendarName is not met and // need to reset here. r->calendar_name_start = 0; r->calendar_name_length = 0; return 0; } return 6 + len + 1; } // CalendarTime_L1: // TimeDesignator TimeSpec [TimeZone] [Calendar] template <typename Char> int32_t ScanCalendarTime_L1(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; if (str.length() < (s + 1)) return 0; // TimeDesignator if (AsciiAlphaToLower(str[cur++]) != 't') return 0; int32_t len = ScanTimeSpec(str, cur, r); if (len == 0) return 0; cur += len; // [TimeZone] cur += ScanTimeZone(str, cur, r); // [Calendar] cur += ScanCalendar(str, cur, r); return cur - s; } // CalendarTime_L2 : // TimeSpecWithOptionalTimeZoneNotAmbiguous [Calendar] template <typename Char> int32_t ScanCalendarTime_L2(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len = ScanTimeSpecWithOptionalTimeZoneNotAmbiguous(str, cur, r); if (len == 0) return 0; cur += len; // [Calendar] cur += ScanCalendar(str, cur, r); return cur - s; } // DateTime: Date [TimeSpecSeparator][TimeZone] template <typename Char> int32_t ScanDateTime(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len = ScanDate(str, cur, r); if (len == 0) return 0; cur += len; cur += ScanTimeSpecSeparator(str, cur, r); cur += ScanTimeZone(str, cur, r); return cur - s; } // DateSpecYearMonth: DateYear ['-'] DateMonth template <typename Char> int32_t ScanDateSpecYearMonth(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t date_year, date_month; int32_t cur = s; int32_t len = ScanDateYear(str, cur, &date_year); if (len == 0) return 0; cur += len; if (str.length() < (cur + 1)) return 0; if (str[cur] == '-') cur++; len = ScanDateMonth(str, cur, &date_month); if (len == 0) return 0; r->date_year = date_year; r->date_month = date_month; cur += len; return cur - s; } // DateSpecMonthDay: // [TwoDash] DateMonth [-] DateDay template <typename Char> int32_t ScanDateSpecMonthDay(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 4)) return 0; int32_t cur = s; if (str[cur] == '-') { // The first two dash are optional together if (str[++cur] != '-') return 0; // TwoDash cur++; } int32_t date_month, date_day; int32_t len = ScanDateMonth(str, cur, &date_month); if (len == 0) return 0; cur += len; if (str.length() < (cur + 1)) return 0; // '-' if (str[cur] == '-') cur++; len = ScanDateDay(str, cur, &date_day); if (len == 0) return 0; r->date_month = date_month; r->date_day = date_day; cur += len; return cur - s; } // TimeZoneIdentifier : // TimeZoneIANAName // TimeZoneUTCOffsetName template <typename Char> int32_t ScanTimeZoneIdentifier(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len; int32_t cur = s; if ((len = ScanTimeZoneIANAName(str, cur)) > 0 || (len = ScanTimeZoneUTCOffsetName(str, cur)) > 0) { cur += len; r->tzi_name_start = s; r->tzi_name_length = len; return cur - s; } return 0; } // CalendarDateTime: DateTime [Calendar] template <typename Char> int32_t ScanCalendarDateTime(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len = ScanDateTime(str, s, r); if (len == 0) return 0; return len + ScanCalendar(str, len, r); } // CalendarDateTimeTimeRequired: Date TimeSpecSeparator [TimeZone] [Calendar] template <typename Char> int32_t ScanCalendarDateTimeTimeRequired(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len = ScanDate(str, cur, r); if (len == 0) return 0; cur += len; len = ScanTimeSpecSeparator(str, cur, r); if (len == 0) return 0; cur += len; // [TimeZone] cur += ScanTimeZone(str, cur, r); // [Calendar] cur += ScanCalendar(str, cur, r); return cur - s; } // TemporalZonedDateTimeString: // Date [TimeSpecSeparator] TimeZoneNameRequired [Calendar] template <typename Char> int32_t ScanTemporalZonedDateTimeString(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { // Date int32_t cur = s; int32_t len = ScanDate(str, cur, r); if (len == 0) return 0; cur += len; // TimeSpecSeparator cur += ScanTimeSpecSeparator(str, cur, r); // TimeZoneNameRequired len = ScanTimeZoneNameRequired(str, cur, r); if (len == 0) return 0; cur += len; // Calendar cur += ScanCalendar(str, cur, r); return cur - s; } SCAN_FORWARD(TemporalDateTimeString, CalendarDateTime, ParsedISO8601Result) // TemporalMonthDayString // DateSpecMonthDay // CalendarDateTime // The lookahead is at most 5 chars. SCAN_EITHER_FORWARD(TemporalMonthDayString, DateSpecMonthDay, CalendarDateTime, ParsedISO8601Result) // TemporalInstantString // Date [TimeSpecSeparator] TimeZoneOffsetRequired [Calendar] template <typename Char> int32_t ScanTemporalInstantString(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { // Date int32_t cur = s; int32_t len = ScanDate(str, cur, r); if (len == 0) return 0; cur += len; // [TimeSpecSeparator] cur += ScanTimeSpecSeparator(str, cur, r); // TimeZoneOffsetRequired len = ScanTimeZoneOffsetRequired(str, cur, r); if (len == 0) return 0; cur += len; // [Calendar] cur += ScanCalendar(str, cur, r); return cur - s; } // ============================================================================== #define SATISIFY(T, R) \ template <typename Char> \ bool Satisfy##T(base::Vector<Char> str, R* r) { \ R ret; \ int32_t len = Scan##T(str, 0, &ret); \ if ((len > 0) && (len == str.length())) { \ *r = ret; \ return true; \ } \ return false; \ } #define IF_SATISFY_RETURN(T) \ { \ if (Satisfy##T(str, r)) return true; \ } #define SATISIFY_EITHER(T1, T2, T3, R) \ template <typename Char> \ bool Satisfy##T1(base::Vector<Char> str, R* r) { \ IF_SATISFY_RETURN(T2) \ IF_SATISFY_RETURN(T3) \ return false; \ } SATISIFY(TemporalDateTimeString, ParsedISO8601Result) SATISIFY(DateTime, ParsedISO8601Result) SATISIFY(DateSpecYearMonth, ParsedISO8601Result) SATISIFY(DateSpecMonthDay, ParsedISO8601Result) SATISIFY(CalendarDateTime, ParsedISO8601Result) SATISIFY(CalendarTime_L1, ParsedISO8601Result) SATISIFY(CalendarTime_L2, ParsedISO8601Result) template <typename Char> bool SatisfyCalendarTime(base::Vector<Char> str, ParsedISO8601Result* r) { IF_SATISFY_RETURN(CalendarTime_L1) IF_SATISFY_RETURN(CalendarTime_L2) return false; } SATISIFY(CalendarDateTimeTimeRequired, ParsedISO8601Result) SATISIFY_EITHER(TemporalTimeString, CalendarTime, CalendarDateTimeTimeRequired, ParsedISO8601Result) SATISIFY_EITHER(TemporalYearMonthString, DateSpecYearMonth, CalendarDateTime, ParsedISO8601Result) SATISIFY_EITHER(TemporalMonthDayString, DateSpecMonthDay, CalendarDateTime, ParsedISO8601Result) SATISIFY(TimeZoneNumericUTCOffset, ParsedISO8601Result) SATISIFY(TimeZoneIdentifier, ParsedISO8601Result) SATISIFY(TemporalInstantString, ParsedISO8601Result) SATISIFY(TemporalZonedDateTimeString, ParsedISO8601Result) SATISIFY(CalendarName, ParsedISO8601Result) // Duration // Digits : Digit [Digits] template <typename Char> int32_t ScanDigits(base::Vector<Char> str, int32_t s, double* out) { if (str.length() < (s + 1) || !IsDecimalDigit(str[s])) return 0; *out = ToInt(str[s]); int32_t len = 1; while (s + len + 1 <= str.length() && IsDecimalDigit(str[s + len])) { *out = 10 * (*out) + ToInt(str[s + len]); len++; } return len; } SCAN_FORWARD(DurationYears, Digits, double) SCAN_FORWARD(DurationMonths, Digits, double) SCAN_FORWARD(DurationWeeks, Digits, double) SCAN_FORWARD(DurationDays, Digits, double) // DurationWholeHours : Digits SCAN_FORWARD(DurationWholeHours, Digits, double) // DurationWholeMinutes : Digits SCAN_FORWARD(DurationWholeMinutes, Digits, double) // DurationWholeSeconds : Digits SCAN_FORWARD(DurationWholeSeconds, Digits, double) // DurationHoursFraction : TimeFraction SCAN_FORWARD(DurationHoursFraction, TimeFraction, int32_t) // DurationMinutesFraction : TimeFraction SCAN_FORWARD(DurationMinutesFraction, TimeFraction, int32_t) // DurationSecondsFraction : TimeFraction SCAN_FORWARD(DurationSecondsFraction, TimeFraction, int32_t) #define DURATION_WHOLE_FRACTION_DESIGNATOR(Name, name, d) \ template <typename Char> \ int32_t ScanDurationWhole##Name##FractionDesignator( \ base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { \ int32_t cur = s; \ double whole = ParsedISO8601Duration::kEmpty; \ cur += ScanDurationWhole##Name(str, cur, &whole); \ if (cur == s) return 0; \ int32_t fraction = ParsedISO8601Duration::kEmpty; \ int32_t len = ScanDuration##Name##Fraction(str, cur, &fraction); \ cur += len; \ if (str.length() < (cur + 1) || AsciiAlphaToLower(str[cur++]) != (d)) \ return 0; \ r->whole_##name = whole; \ r->name##_fraction = fraction; \ return cur - s; \ } DURATION_WHOLE_FRACTION_DESIGNATOR(Seconds, seconds, 's') DURATION_WHOLE_FRACTION_DESIGNATOR(Minutes, minutes, 'm') DURATION_WHOLE_FRACTION_DESIGNATOR(Hours, hours, 'h') // DurationSecondsPart : // DurationWholeSeconds DurationSecondsFractionopt SecondsDesignator SCAN_FORWARD(DurationSecondsPart, DurationWholeSecondsFractionDesignator, ParsedISO8601Duration) // DurationMinutesPart : // DurationWholeMinutes DurationMinutesFractionopt MinutesDesignator // [DurationSecondsPart] template <typename Char> int32_t ScanDurationMinutesPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len = ScanDurationWholeMinutesFractionDesignator(str, s, r); if (len == 0) return 0; cur += len; cur += ScanDurationSecondsPart(str, cur, r); return cur - s; } // DurationHoursPart : // DurationWholeHours DurationHoursFractionopt HoursDesignator // DurationMinutesPart // // DurationWholeHours DurationHoursFractionopt HoursDesignator // [DurationSecondsPart] template <typename Char> int32_t ScanDurationHoursPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len = ScanDurationWholeHoursFractionDesignator(str, s, r); if (len == 0) return 0; cur += len; len = ScanDurationMinutesPart(str, cur, r); if (len > 0) { cur += len; } else { cur += ScanDurationSecondsPart(str, cur, r); } return cur - s; } // DurationTime : // TimeDesignator DurationHoursPart // TimeDesignator DurationMinutesPart // TimeDesignator DurationSecondsPart template <typename Char> int32_t ScanDurationTime(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; if (str.length() < (s + 1)) return 0; if (AsciiAlphaToLower(str[cur++]) != 't') return 0; if ((cur += ScanDurationHoursPart(str, cur, r)) - s > 1) return cur - s; if ((cur += ScanDurationMinutesPart(str, cur, r)) - s > 1) return cur - s; if ((cur += ScanDurationSecondsPart(str, cur, r)) - s > 1) return cur - s; return 0; } #define DURATION_AND_DESIGNATOR(Name, name, d) \ template <typename Char> \ int32_t ScanDuration##Name##Designator(base::Vector<Char> str, int32_t s, \ ParsedISO8601Duration* r) { \ int32_t cur = s; \ double name; \ if ((cur += ScanDuration##Name(str, cur, &name)) == s) return 0; \ if (str.length() < (cur + 1) || AsciiAlphaToLower(str[cur++]) != (d)) { \ return 0; \ } \ r->name = name; \ return cur - s; \ } DURATION_AND_DESIGNATOR(Days, days, 'd') DURATION_AND_DESIGNATOR(Weeks, weeks, 'w') DURATION_AND_DESIGNATOR(Months, months, 'm') DURATION_AND_DESIGNATOR(Years, years, 'y') // DurationDaysPart : DurationDays DaysDesignator SCAN_FORWARD(DurationDaysPart, DurationDaysDesignator, ParsedISO8601Duration) // DurationWeeksPart : DurationWeeks WeeksDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationWeeksPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; if ((cur += ScanDurationWeeksDesignator(str, cur, r)) == s) return 0; cur += ScanDurationDaysPart(str, cur, r); return cur - s; } // DurationMonthsPart : // DurationMonths MonthsDesignator DurationWeeksPart // DurationMonths MonthsDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationMonthsPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len = ScanDurationMonthsDesignator(str, cur, r); if (len == 0) return 0; cur += len; if ((len = ScanDurationWeeksPart(str, cur, r)) > 0) { cur += len; } else { cur += ScanDurationDaysPart(str, cur, r); } return cur - s; } // DurationYearsPart : // DurationYears YearsDesignator DurationMonthsPart // DurationYears YearsDesignator DurationWeeksPart // DurationYears YearsDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationYearsPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len = ScanDurationYearsDesignator(str, cur, r); if (len == 0) return 0; cur += len; if ((len = ScanDurationMonthsPart(str, cur, r)) > 0) { cur += len; } else if ((len = ScanDurationWeeksPart(str, cur, r)) > 0) { cur += len; } else { len = ScanDurationDaysPart(str, cur, r); cur += len; } return cur - s; } // DurationDate : // DurationYearsPart [DurationTime] // DurationMonthsPart [DurationTime] // DurationWeeksPart [DurationTime] // DurationDaysPart [DurationTime] template <typename Char> int32_t ScanDurationDate(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; do { if ((cur += ScanDurationYearsPart(str, cur, r)) > s) break; if ((cur += ScanDurationMonthsPart(str, cur, r)) > s) break; if ((cur += ScanDurationWeeksPart(str, cur, r)) > s) break; if ((cur += ScanDurationDaysPart(str, cur, r)) > s) break; return 0; } while (false); cur += ScanDurationTime(str, cur, r); return cur - s; } // Duration : // Signopt DurationDesignator DurationDate // Signopt DurationDesignator DurationTime template <typename Char> int32_t ScanDuration(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { if (str.length() < (s + 2)) return 0; int32_t cur = s; int32_t sign = (IsSign(str[cur]) && CanonicalSign(str[cur++]) == '-') ? -1 : 1; if (AsciiAlphaToLower(str[cur++]) != 'p') return 0; int32_t len = ScanDurationDate(str, cur, r); if (len == 0) len = ScanDurationTime(str, cur, r); if (len == 0) return 0; r->sign = sign; cur += len; return cur - s; } SCAN_FORWARD(TemporalDurationString, Duration, ParsedISO8601Duration) SATISIFY(TemporalDurationString, ParsedISO8601Duration) } // namespace #define IMPL_PARSE_METHOD(R, NAME) \ base::Optional<R> TemporalParser::Parse##NAME(Isolate* isolate, \ Handle<String> iso_string) { \ bool valid; \ R parsed; \ iso_string = String::Flatten(isolate, iso_string); \ { \ DisallowGarbageCollection no_gc; \ String::FlatContent str_content = iso_string->GetFlatContent(no_gc); \ if (str_content.IsOneByte()) { \ valid = Satisfy##NAME(str_content.ToOneByteVector(), &parsed); \ } else { \ valid = Satisfy##NAME(str_content.ToUC16Vector(), &parsed); \ } \ } \ if (valid) return parsed; \ return base::nullopt; \ } IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalDateTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalYearMonthString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalMonthDayString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalInstantString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalZonedDateTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TimeZoneIdentifier) IMPL_PARSE_METHOD(ParsedISO8601Result, CalendarName) IMPL_PARSE_METHOD(ParsedISO8601Result, TimeZoneNumericUTCOffset) IMPL_PARSE_METHOD(ParsedISO8601Duration, TemporalDurationString) } // namespace internal } // namespace v8