ladybird/Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp
2022-05-16 20:29:32 +01:00

1764 lines
55 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/CharacterTypes.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
namespace JS::Temporal {
namespace Detail {
// https://tc39.es/proposal-temporal/#prod-DecimalDigits
bool ISO8601Parser::parse_decimal_digits()
{
// DecimalDigits[Sep] ::
// DecimalDigit
// DecimalDigits[?Sep] DecimalDigit
// [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator DecimalDigit
// NOTE: Temporal exclusively uses the variant without a separator ([~Sep])
if (!parse_decimal_digit())
return false;
while (parse_decimal_digit())
;
return true;
}
// https://tc39.es/proposal-temporal/#prod-DecimalDigit
bool ISO8601Parser::parse_decimal_digit()
{
// DecimalDigit : one of
// 0 1 2 3 4 5 6 7 8 9
if (m_state.lexer.next_is(is_ascii_digit)) {
m_state.lexer.consume();
return true;
}
return false;
}
// https://tc39.es/proposal-temporal/#prod-NonZeroDigit
bool ISO8601Parser::parse_non_zero_digit()
{
// NonZeroDigit : one of
// 1 2 3 4 5 6 7 8 9
if (m_state.lexer.next_is(is_ascii_digit) && !m_state.lexer.next_is('0')) {
m_state.lexer.consume();
return true;
}
return false;
}
// https://tc39.es/proposal-temporal/#prod-ASCIISign
bool ISO8601Parser::parse_ascii_sign()
{
// ASCIISign : one of
// + -
return m_state.lexer.consume_specific('+')
|| m_state.lexer.consume_specific('-');
}
// https://tc39.es/proposal-temporal/#prod-Sign
bool ISO8601Parser::parse_sign()
{
// Sign :
// ASCIISign
// U+2212
StateTransaction transaction { *this };
auto success = parse_ascii_sign()
|| m_state.lexer.consume_specific("\xE2\x88\x92"sv);
if (!success)
return false;
m_state.parse_result.sign = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-UnpaddedHour
bool ISO8601Parser::parse_unpadded_hour()
{
// UnpaddedHour :
// DecimalDigit
// 1 DecimalDigit
// 20
// 21
// 22
// 23
StateTransaction transaction { *this };
auto success = m_state.lexer.consume_specific("20"sv)
|| m_state.lexer.consume_specific("21"sv)
|| m_state.lexer.consume_specific("22"sv)
|| m_state.lexer.consume_specific("23"sv);
if (!success) {
// This could be either of the first two productions.
if (m_state.lexer.consume_specific('1'))
(void)parse_decimal_digit();
else if (!parse_decimal_digit())
return false;
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-Hour
bool ISO8601Parser::parse_hour()
{
// Hour :
// 0 DecimalDigit
// 1 DecimalDigit
// 20
// 21
// 22
// 23
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('0') || m_state.lexer.consume_specific('1')) {
if (!parse_decimal_digit())
return false;
} else {
auto success = m_state.lexer.consume_specific("20"sv)
|| m_state.lexer.consume_specific("21"sv)
|| m_state.lexer.consume_specific("22"sv)
|| m_state.lexer.consume_specific("23"sv);
if (!success)
return false;
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-MinuteSecond
bool ISO8601Parser::parse_minute_second()
{
// MinuteSecond :
// 0 DecimalDigit
// 1 DecimalDigit
// 2 DecimalDigit
// 3 DecimalDigit
// 4 DecimalDigit
// 5 DecimalDigit
StateTransaction transaction { *this };
auto success = m_state.lexer.consume_specific('0')
|| m_state.lexer.consume_specific('1')
|| m_state.lexer.consume_specific('2')
|| m_state.lexer.consume_specific('3')
|| m_state.lexer.consume_specific('4')
|| m_state.lexer.consume_specific('5');
if (!success)
return false;
if (!parse_decimal_digit())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DecimalSeparator
bool ISO8601Parser::parse_decimal_separator()
{
// DecimalSeparator : one of
// . ,
return m_state.lexer.consume_specific('.')
|| m_state.lexer.consume_specific(',');
}
// https://tc39.es/proposal-temporal/#prod-DaysDesignator
bool ISO8601Parser::parse_days_designator()
{
// DaysDesignator : one of
// D d
return m_state.lexer.consume_specific('D')
|| m_state.lexer.consume_specific('d');
}
// https://tc39.es/proposal-temporal/#prod-HoursDesignator
bool ISO8601Parser::parse_hours_designator()
{
// HoursDesignator : one of
// H h
return m_state.lexer.consume_specific('H')
|| m_state.lexer.consume_specific('h');
}
// https://tc39.es/proposal-temporal/#prod-MinutesDesignator
bool ISO8601Parser::parse_minutes_designator()
{
// MinutesDesignator : one of
// M m
return m_state.lexer.consume_specific('M')
|| m_state.lexer.consume_specific('m');
}
// https://tc39.es/proposal-temporal/#prod-MonthsDesignator
bool ISO8601Parser::parse_months_designator()
{
// MonthsDesignator : one of
// M m
return m_state.lexer.consume_specific('M')
|| m_state.lexer.consume_specific('m');
}
// https://tc39.es/proposal-temporal/#prod-DurationDesignator
bool ISO8601Parser::parse_duration_designator()
{
// DurationDesignator : one of
// P p
return m_state.lexer.consume_specific('P')
|| m_state.lexer.consume_specific('p');
}
// https://tc39.es/proposal-temporal/#prod-SecondsDesignator
bool ISO8601Parser::parse_seconds_designator()
{
// SecondsDesignator : one of
// S s
return m_state.lexer.consume_specific('S')
|| m_state.lexer.consume_specific('s');
}
// https://tc39.es/proposal-temporal/#prod-DateTimeSeparator
bool ISO8601Parser::parse_date_time_separator()
{
// DateTimeSeparator :
// <SP>
// T
// t
return m_state.lexer.consume_specific(' ')
|| m_state.lexer.consume_specific('T')
|| m_state.lexer.consume_specific('t');
}
// https://tc39.es/proposal-temporal/#prod-TimeDesignator
bool ISO8601Parser::parse_time_designator()
{
// TimeDesignator : one of
// T t
return m_state.lexer.consume_specific('T')
|| m_state.lexer.consume_specific('t');
}
// https://tc39.es/proposal-temporal/#prod-WeeksDesignator
bool ISO8601Parser::parse_weeks_designator()
{
// WeeksDesignator : one of
// W w
return m_state.lexer.consume_specific('W')
|| m_state.lexer.consume_specific('w');
}
// https://tc39.es/proposal-temporal/#prod-YearsDesignator
bool ISO8601Parser::parse_years_designator()
{
// YearsDesignator : one of
// Y y
return m_state.lexer.consume_specific('Y')
|| m_state.lexer.consume_specific('y');
}
// https://tc39.es/proposal-temporal/#prod-UTCDesignator
bool ISO8601Parser::parse_utc_designator()
{
// UTCDesignator : one of
// Z z
StateTransaction transaction { *this };
auto success = m_state.lexer.consume_specific('Z')
|| m_state.lexer.consume_specific('z');
if (!success)
return false;
m_state.parse_result.utc_designator = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateYear
bool ISO8601Parser::parse_date_year()
{
// DateFourDigitYear :
// DecimalDigit DecimalDigit DecimalDigit DecimalDigit
// DateExtendedYear :
// Sign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
// DateYear :
// DateFourDigitYear
// DateExtendedYear
StateTransaction transaction { *this };
if (parse_sign()) {
for (size_t i = 0; i < 6; ++i) {
if (!parse_decimal_digit())
return false;
}
} else {
for (size_t i = 0; i < 4; ++i) {
if (!parse_decimal_digit())
return false;
}
}
// It is a Syntax Error if DateExtendedYear is "-000000" or "000000" (U+2212 MINUS SIGN followed by 000000).
if (transaction.parsed_string_view().is_one_of("-000000"sv, "000000"sv))
return false;
m_state.parse_result.date_year = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateMonth
bool ISO8601Parser::parse_date_month()
{
// DateMonth :
// 0 NonZeroDigit
// 10
// 11
// 12
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('0')) {
if (!parse_non_zero_digit())
return false;
} else {
auto success = m_state.lexer.consume_specific("10"sv)
|| m_state.lexer.consume_specific("11"sv)
|| m_state.lexer.consume_specific("12"sv);
if (!success)
return false;
}
m_state.parse_result.date_month = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateDay
bool ISO8601Parser::parse_date_day()
{
// DateDay :
// 0 NonZeroDigit
// 1 DecimalDigit
// 2 DecimalDigit
// 30
// 31
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('0')) {
if (!parse_non_zero_digit())
return false;
} else if (m_state.lexer.consume_specific('1') || m_state.lexer.consume_specific('2')) {
if (!parse_decimal_digit())
return false;
} else {
auto success = m_state.lexer.consume_specific("30"sv)
|| m_state.lexer.consume_specific("31"sv);
if (!success)
return false;
}
m_state.parse_result.date_day = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateSpecYearMonth
bool ISO8601Parser::parse_date_spec_year_month()
{
// DateSpecYearMonth :
// DateYear -[opt] DateMonth
StateTransaction transaction { *this };
if (!parse_date_year())
return false;
m_state.lexer.consume_specific('-');
if (!parse_date_month())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateSpecMonthDay
bool ISO8601Parser::parse_date_spec_month_day()
{
// TwoDashes :
// --
// DateSpecMonthDay :
// TwoDashes[opt] DateMonth -[opt] DateDay
StateTransaction transaction { *this };
m_state.lexer.consume_specific("--"sv);
if (!parse_date_month())
return false;
m_state.lexer.consume_specific('-');
if (!parse_date_day())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-Date
bool ISO8601Parser::parse_date()
{
// Date :
// DateYear - DateMonth - DateDay
// DateYear DateMonth DateDay
StateTransaction transaction { *this };
if (!parse_date_year())
return false;
auto with_dashes = m_state.lexer.consume_specific('-');
if (!parse_date_month())
return false;
if (with_dashes && !m_state.lexer.consume_specific('-'))
return false;
if (!parse_date_day())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeHour
bool ISO8601Parser::parse_time_hour()
{
// TimeHour :
// Hour
StateTransaction transaction { *this };
if (!parse_hour())
return false;
m_state.parse_result.time_hour = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeMinute
bool ISO8601Parser::parse_time_minute()
{
// TimeMinute :
// MinuteSecond
StateTransaction transaction { *this };
if (!parse_minute_second())
return false;
m_state.parse_result.time_minute = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeSecond
bool ISO8601Parser::parse_time_second()
{
// TimeSecond :
// MinuteSecond
// 60
StateTransaction transaction { *this };
auto success = parse_minute_second()
|| m_state.lexer.consume_specific("60"sv);
if (!success)
return false;
m_state.parse_result.time_second = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeHourNotValidMonth
bool ISO8601Parser::parse_time_hour_not_valid_month()
{
// TimeHourNotValidMonth : one of
// 00 13 14 15 16 17 18 19 20 21 23
StateTransaction transaction { *this };
auto success = m_state.lexer.consume_specific("00"sv)
|| m_state.lexer.consume_specific("13"sv)
|| m_state.lexer.consume_specific("14"sv)
|| m_state.lexer.consume_specific("15"sv)
|| m_state.lexer.consume_specific("16"sv)
|| m_state.lexer.consume_specific("17"sv)
|| m_state.lexer.consume_specific("18"sv)
|| m_state.lexer.consume_specific("19"sv)
|| m_state.lexer.consume_specific("20"sv)
|| m_state.lexer.consume_specific("21"sv)
|| m_state.lexer.consume_specific("22"sv)
|| m_state.lexer.consume_specific("23"sv);
if (!success)
return false;
m_state.parse_result.time_hour_not_valid_month = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeHourNotThirtyOneDayMonth
bool ISO8601Parser::parse_time_hour_not_thirty_one_day_month()
{
// TimeHourNotThirtyOneDayMonth : one of
// 02 04 06 09 11
StateTransaction transaction { *this };
auto success = m_state.lexer.consume_specific("02"sv)
|| m_state.lexer.consume_specific("04"sv)
|| m_state.lexer.consume_specific("06"sv)
|| m_state.lexer.consume_specific("09"sv)
|| m_state.lexer.consume_specific("11"sv);
if (!success)
return false;
m_state.parse_result.time_hour_not_thirty_one_day_month = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeHourTwoOnly
bool ISO8601Parser::parse_time_hour_two_only()
{
// TimeHourTwoOnly :
// 02
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific("02"sv))
return false;
m_state.parse_result.time_hour_two_only = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeMinuteNotValidDay
bool ISO8601Parser::parse_time_minute_not_valid_day()
{
// TimeMinuteNotValidDay :
// 00
// 32
// 33
// 34
// 35
// 36
// 37
// 38
// 39
// 4 DecimalDigit
// 5 DecimalDigit
// 60
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('4') || m_state.lexer.consume_specific('5')) {
if (!parse_decimal_digit())
return false;
} else {
auto success = m_state.lexer.consume_specific("00"sv)
|| m_state.lexer.consume_specific("32"sv)
|| m_state.lexer.consume_specific("33"sv)
|| m_state.lexer.consume_specific("34"sv)
|| m_state.lexer.consume_specific("35"sv)
|| m_state.lexer.consume_specific("36"sv)
|| m_state.lexer.consume_specific("37"sv)
|| m_state.lexer.consume_specific("38"sv)
|| m_state.lexer.consume_specific("39"sv)
|| m_state.lexer.consume_specific("60"sv);
if (!success)
return false;
}
m_state.parse_result.time_minute_not_valid_day = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeMinuteThirtyOnly
bool ISO8601Parser::parse_time_minute_thirty_only()
{
// TimeMinuteThirtyOnly :
// 30
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific("30"sv))
return false;
m_state.parse_result.time_minute_thirty_only = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeMinuteThirtyOneOnly
bool ISO8601Parser::parse_time_minute_thirty_one_only()
{
// TimeMinuteThirtyOneOnly :
// 31
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific("31"sv))
return false;
m_state.parse_result.time_minute_thirty_one_only = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeSecondNotValidMonth
bool ISO8601Parser::parse_time_second_not_valid_month()
{
// TimeSecondNotValidMonth :
// 00
// 13
// 14
// 15
// 16
// 17
// 18
// 19
// 2 DecimalDigit
// 3 DecimalDigit
// 4 DecimalDigit
// 5 DecimalDigit
// 60
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('2')
|| m_state.lexer.consume_specific('3')
|| m_state.lexer.consume_specific('4')
|| m_state.lexer.consume_specific('5')) {
if (!parse_decimal_digit())
return false;
} else {
auto success = m_state.lexer.consume_specific("00"sv)
|| m_state.lexer.consume_specific("13"sv)
|| m_state.lexer.consume_specific("14"sv)
|| m_state.lexer.consume_specific("15"sv)
|| m_state.lexer.consume_specific("16"sv)
|| m_state.lexer.consume_specific("17"sv)
|| m_state.lexer.consume_specific("18"sv)
|| m_state.lexer.consume_specific("19"sv)
|| m_state.lexer.consume_specific("60"sv);
if (!success)
return false;
}
m_state.parse_result.time_second_not_valid_month = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-FractionalPart
bool ISO8601Parser::parse_fractional_part()
{
// FractionalPart :
// DecimalDigit DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt]
if (!parse_decimal_digit())
return false;
for (size_t i = 0; i < 8; ++i) {
if (!parse_decimal_digit())
break;
}
return true;
}
// https://tc39.es/proposal-temporal/#prod-Fraction
bool ISO8601Parser::parse_fraction()
{
// Fraction :
// DecimalSeparator FractionalPart
StateTransaction transaction { *this };
if (!parse_decimal_separator())
return false;
if (!parse_fractional_part())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeFraction
bool ISO8601Parser::parse_time_fraction()
{
// TimeFraction :
// Fraction
StateTransaction transaction { *this };
if (!parse_fraction())
return false;
m_state.parse_result.time_fraction = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSign
bool ISO8601Parser::parse_time_zone_utc_offset_sign()
{
// TimeZoneUTCOffsetSign :
// Sign
StateTransaction transaction { *this };
if (!parse_sign())
return false;
m_state.parse_result.time_zone_utc_offset_sign = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetHour
bool ISO8601Parser::parse_time_zone_utc_offset_hour()
{
// TimeZoneUTCOffsetHour :
// Hour
StateTransaction transaction { *this };
if (!parse_hour())
return false;
m_state.parse_result.time_zone_utc_offset_hour = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetMinute
bool ISO8601Parser::parse_time_zone_utc_offset_minute()
{
// TimeZoneUTCOffsetMinute :
// MinuteSecond
StateTransaction transaction { *this };
if (!parse_minute_second())
return false;
m_state.parse_result.time_zone_utc_offset_minute = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSecond
bool ISO8601Parser::parse_time_zone_utc_offset_second()
{
// TimeZoneUTCOffsetSecond :
// MinuteSecond
StateTransaction transaction { *this };
if (!parse_minute_second())
return false;
m_state.parse_result.time_zone_utc_offset_second = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetFractionalPart
bool ISO8601Parser::parse_time_zone_utc_offset_fractional_part()
{
// TimeZoneUTCOffsetFractionalPart :
// FractionalPart
StateTransaction transaction { *this };
if (!parse_fractional_part())
return false;
m_state.parse_result.time_zone_utc_offset_fractional_part = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetFraction
bool ISO8601Parser::parse_time_zone_utc_offset_fraction()
{
// TimeZoneUTCOffsetFraction :
// DecimalSeparator TimeZoneUTCOffsetFractionalPart
StateTransaction transaction { *this };
if (!parse_decimal_separator())
return false;
if (!parse_time_zone_utc_offset_fractional_part())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffset
bool ISO8601Parser::parse_time_zone_numeric_utc_offset()
{
// TimeZoneNumericUTCOffset :
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
StateTransaction transaction { *this };
if (!parse_time_zone_utc_offset_sign())
return false;
if (!parse_time_zone_utc_offset_hour())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_zone_utc_offset_minute())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_zone_utc_offset_second())
return false;
(void)parse_time_zone_utc_offset_fraction();
}
} else if (parse_time_zone_utc_offset_minute()) {
if (parse_time_zone_utc_offset_second())
(void)parse_time_zone_utc_offset_fraction();
}
m_state.parse_result.time_zone_numeric_utc_offset = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffset
bool ISO8601Parser::parse_time_zone_utc_offset()
{
// TimeZoneUTCOffset :
// TimeZoneNumericUTCOffset
// UTCDesignator
return parse_time_zone_numeric_utc_offset()
|| parse_utc_designator();
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffsetNotAmbiguous
bool ISO8601Parser::parse_time_zone_numeric_utc_offset_not_ambiguous()
{
// TimeZoneNumericUTCOffsetNotAmbiguous :
// + TimeZoneUTCOffsetHour
// U+2212 TimeZoneUTCOffsetHour
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
// TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
StateTransaction transaction { *this };
if (m_state.lexer.consume_specific('+') || m_state.lexer.consume_specific("\xE2\x88\x92"sv)) {
if (!parse_time_zone_utc_offset_hour())
return false;
} else {
if (!parse_time_zone_utc_offset_sign())
return false;
if (!parse_time_zone_utc_offset_hour())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_zone_utc_offset_minute())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_zone_utc_offset_second())
return false;
(void)parse_time_zone_utc_offset_fraction();
}
} else {
if (!parse_time_zone_utc_offset_minute())
return false;
if (parse_time_zone_utc_offset_second())
(void)parse_time_zone_utc_offset_fraction();
}
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour
bool ISO8601Parser::parse_time_zone_numeric_utc_offset_not_ambiguous_allowed_negative_hour()
{
// TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour :
// TimeZoneNumericUTCOffsetNotAmbiguous
// - TimeHourNotValidMonth
StateTransaction transaction { *this };
if (!parse_time_zone_numeric_utc_offset_not_ambiguous()) {
if (!m_state.lexer.consume_specific('-'))
return false;
if (!parse_time_hour_not_valid_month())
return false;
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetName
bool ISO8601Parser::parse_time_zone_utc_offset_name()
{
// TimeZoneUTCOffsetName :
// Sign Hour
// Sign Hour : MinuteSecond
// Sign Hour MinuteSecond
// Sign Hour : MinuteSecond : MinuteSecond Fraction[opt]
// Sign Hour MinuteSecond MinuteSecond Fraction[opt]
StateTransaction transaction { *this };
if (!parse_sign())
return false;
if (!parse_hour())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_minute_second())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_minute_second())
return false;
(void)parse_fraction();
}
} else if (parse_minute_second()) {
if (parse_minute_second())
(void)parse_fraction();
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TZLeadingChar
bool ISO8601Parser::parse_tz_leading_char()
{
// TZLeadingChar :
// Alpha
// .
// _
if (m_state.lexer.next_is(is_ascii_alpha)) {
m_state.lexer.consume();
return true;
}
return m_state.lexer.consume_specific('.')
|| m_state.lexer.consume_specific('_');
}
// https://tc39.es/proposal-temporal/#prod-TZChar
bool ISO8601Parser::parse_tz_char()
{
// TZChar :
// Alpha
// .
// -
// _
if (m_state.lexer.next_is(is_ascii_alpha)) {
m_state.lexer.consume();
return true;
}
return m_state.lexer.consume_specific('.')
|| m_state.lexer.consume_specific('-')
|| m_state.lexer.consume_specific('_');
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameComponent
bool ISO8601Parser::parse_time_zone_iana_component()
{
// TimeZoneIANANameComponent :
// TZLeadingChar TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] but not one of . or ..
StateTransaction transaction { *this };
if (!parse_tz_leading_char())
return false;
for (size_t i = 0; i < 13; ++i) {
if (!parse_tz_char())
break;
}
if (transaction.parsed_string_view().is_one_of("."sv, ".."sv))
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameTail
bool ISO8601Parser::parse_time_zone_iana_name_tail()
{
// TimeZoneIANANameTail :
// TimeZoneIANANameComponent
// TimeZoneIANANameComponent / TimeZoneIANANameTail
StateTransaction transaction { *this };
if (!parse_time_zone_iana_component())
return false;
while (m_state.lexer.next_is('/')) {
m_state.lexer.consume();
if (!parse_time_zone_iana_component())
return false;
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneIANAName
bool ISO8601Parser::parse_time_zone_iana_name()
{
// TimeZoneIANAName :
// Etc/GMT ASCIISign UnpaddedHour
// TimeZoneIANANameTail but not Etc/GMT ASCIISign UnpaddedHour
auto parse_etc_gmt_with_offset = [this] {
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific("Etc/GMT"sv))
return false;
if (!parse_ascii_sign())
return false;
if (!parse_unpadded_hour())
return false;
transaction.commit();
return true;
};
StateTransaction transaction { *this };
if (parse_etc_gmt_with_offset()) {
// no-op.
} else if (!parse_time_zone_iana_name_tail()) {
return false;
}
m_state.parse_result.time_zone_iana_name = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedName
bool ISO8601Parser::parse_time_zone_bracketed_name()
{
// TimeZoneBracketedName :
// TimeZoneIANAName
// TimeZoneUTCOffsetName
StateTransaction transaction { *this };
if (parse_time_zone_iana_name()) {
// no-op.
} else if (!parse_time_zone_utc_offset_name()) {
return false;
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedAnnotation
bool ISO8601Parser::parse_time_zone_bracketed_annotation()
{
// TimeZoneBracketedAnnotation :
// [ TimeZoneBracketedName ]
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific('['))
return false;
if (!parse_time_zone_bracketed_name())
return false;
if (!m_state.lexer.consume_specific(']'))
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneOffsetRequired
bool ISO8601Parser::parse_time_zone_offset_required()
{
// TimeZoneOffsetRequired :
// TimeZoneUTCOffset TimeZoneBracketedAnnotation[opt]
StateTransaction transaction { *this };
if (!parse_time_zone_utc_offset())
return false;
(void)parse_time_zone_bracketed_annotation();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZoneNameRequired
bool ISO8601Parser::parse_time_zone_name_required()
{
// TimeZoneNameRequired :
// TimeZoneUTCOffset[opt] TimeZoneBracketedAnnotation
StateTransaction transaction { *this };
(void)parse_time_zone_utc_offset();
if (!parse_time_zone_bracketed_annotation())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeZone
bool ISO8601Parser::parse_time_zone()
{
// TimeZone :
// TimeZoneUTCOffset TimeZoneBracketedAnnotation[opt]
// TimeZoneBracketedAnnotation
StateTransaction transaction { *this };
if (parse_time_zone_utc_offset())
(void)parse_time_zone_bracketed_annotation();
else if (!parse_time_zone_bracketed_annotation())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-CalendarName
bool ISO8601Parser::parse_calendar_name()
{
// CalChar :
// Alpha
// DecimalDigit
// CalendarNameComponent :
// CalChar CalChar CalChar CalChar[opt] CalChar[opt] CalChar[opt] CalChar[opt] CalChar[opt]
// CalendarNameTail :
// CalendarNameComponent
// CalendarNameComponent - CalendarNameTail
// CalendarName :
// CalendarNameTail
auto parse_calendar_name_component = [&] {
for (size_t i = 0; i < 8; ++i) {
if (!m_state.lexer.next_is(is_ascii_alphanumeric))
return i > 2;
m_state.lexer.consume();
}
return true;
};
StateTransaction transaction { *this };
do {
if (!parse_calendar_name_component())
return false;
} while (m_state.lexer.consume_specific('-'));
m_state.parse_result.calendar_name = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-Calendar
bool ISO8601Parser::parse_calendar()
{
// Calendar :
// [u-ca= CalendarName ]
StateTransaction transaction { *this };
if (!m_state.lexer.consume_specific("[u-ca="sv))
return false;
if (!parse_calendar_name())
return false;
if (!m_state.lexer.consume_specific(']'))
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeSpec
bool ISO8601Parser::parse_time_spec()
{
// TimeSpec :
// TimeHour
// TimeHour : TimeMinute
// TimeHour TimeMinute
// TimeHour : TimeMinute : TimeSecond TimeFraction[opt]
// TimeHour TimeMinute TimeSecond TimeFraction[opt]
StateTransaction transaction { *this };
if (!parse_time_hour())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_minute())
return false;
if (m_state.lexer.consume_specific(':')) {
if (!parse_time_second())
return false;
(void)parse_time_fraction();
}
} else if (parse_time_minute()) {
if (parse_time_second())
(void)parse_time_fraction();
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TimeHourMinuteBasicFormatNotAmbiguous
bool ISO8601Parser::parse_time_hour_minute_basic_format_not_ambiguous()
{
// TimeHourMinuteBasicFormatNotAmbiguous :
// TimeHourNotValidMonth TimeMinute
// TimeHour TimeMinuteNotValidDay
// TimeHourNotThirtyOneDayMonth TimeMinuteThirtyOneOnly
// TimeHourTwoOnly TimeMinuteThirtyOnly
{
StateTransaction transaction { *this };
if (parse_time_hour_not_valid_month() && parse_time_minute()) {
transaction.commit();
return true;
}
}
{
StateTransaction transaction { *this };
if (parse_time_hour() && parse_time_minute_not_valid_day()) {
transaction.commit();
return true;
}
}
{
StateTransaction transaction { *this };
if (parse_time_hour_not_thirty_one_day_month() && parse_time_minute_thirty_one_only()) {
transaction.commit();
return true;
}
}
{
StateTransaction transaction { *this };
if (parse_time_hour_two_only() && parse_time_minute_thirty_only()) {
transaction.commit();
return true;
}
}
return false;
}
// https://tc39.es/proposal-temporal/#prod-TimeSpecWithOptionalTimeZoneNotAmbiguous
bool ISO8601Parser::parse_time_spec_with_optional_time_zone_not_ambiguous()
{
// TimeSpecWithOptionalTimeZoneNotAmbiguous :
// TimeHour TimeZoneNumericUTCOffsetNotAmbiguous[opt] TimeZoneBracketedAnnotation[opt]
// TimeHourNotValidMonth TimeZone
// TimeHour : TimeMinute TimeZone[opt]
// TimeHourMinuteBasicFormatNotAmbiguous TimeZoneBracketedAnnotation[opt]
// TimeHour TimeMinute TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour TimeZoneBracketedAnnotation[opt]
// TimeHour : TimeMinute : TimeSecond TimeFraction[opt] TimeZone[opt]
// TimeHour TimeMinute TimeSecondNotValidMonth TimeZone[opt]
// TimeHour TimeMinute TimeSecond TimeFraction TimeZone[opt]
// NOTE: Reverse order here because `TimeHour TimeZoneNumericUTCOffsetNotAmbiguous[opt] TimeZoneBracketedAnnotation[opt]` can
// be a subset of `TimeHourNotValidMonth TimeZone`, so we'd not attempt to parse that but may not exhaust the input string.
{
StateTransaction transaction { *this };
if (parse_time_hour_not_valid_month() && parse_time_zone()) {
transaction.commit();
return true;
}
}
{
StateTransaction transaction { *this };
if (parse_time_hour()) {
if (m_state.lexer.consume_specific(':')) {
if (parse_time_minute()) {
if (m_state.lexer.consume_specific(':')) {
if (parse_time_second()) {
(void)parse_time_fraction();
(void)parse_time_zone();
transaction.commit();
return true;
}
} else {
(void)parse_time_zone();
transaction.commit();
return true;
}
}
} else if (parse_time_minute()) {
if (parse_time_zone_numeric_utc_offset_not_ambiguous_allowed_negative_hour()) {
(void)parse_time_zone_bracketed_annotation();
transaction.commit();
return true;
}
{
StateTransaction sub_transaction { *this };
if (parse_time_second() && parse_time_fraction()) {
(void)parse_time_zone();
transaction.commit();
return true;
}
}
if (parse_time_second_not_valid_month()) {
(void)parse_time_zone();
transaction.commit();
return true;
}
} else {
(void)parse_time_zone_numeric_utc_offset_not_ambiguous();
(void)parse_time_zone_bracketed_annotation();
transaction.commit();
return true;
}
}
}
{
StateTransaction transaction { *this };
if (parse_time_hour_minute_basic_format_not_ambiguous()) {
(void)parse_time_zone_bracketed_annotation();
transaction.commit();
return true;
}
}
return false;
}
// https://tc39.es/proposal-temporal/#prod-TimeSpecSeparator
bool ISO8601Parser::parse_time_spec_separator()
{
// TimeSpecSeparator :
// DateTimeSeparator TimeSpec
StateTransaction transaction { *this };
if (!parse_date_time_separator())
return false;
if (!parse_time_spec())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DateTime
bool ISO8601Parser::parse_date_time()
{
// DateTime :
// Date TimeSpecSeparator[opt] TimeZone[opt]
if (!parse_date())
return false;
(void)parse_time_spec_separator();
(void)parse_time_zone();
return true;
}
// https://tc39.es/proposal-temporal/#prod-CalendarTime
bool ISO8601Parser::parse_calendar_time()
{
// CalendarTime :
// TimeDesignator TimeSpec TimeZone[opt] Calendar[opt]
// TimeSpec TimeZone[opt] Calendar
// TimeSpecWithOptionalTimeZoneNotAmbiguous
{
StateTransaction transaction { *this };
if (parse_time_designator() && parse_time_spec()) {
(void)parse_time_zone();
(void)parse_calendar();
transaction.commit();
return true;
}
}
{
StateTransaction transaction { *this };
if (parse_time_spec()) {
(void)parse_time_zone();
if (parse_calendar()) {
transaction.commit();
return true;
}
}
}
return parse_time_spec_with_optional_time_zone_not_ambiguous();
}
// https://tc39.es/proposal-temporal/#prod-CalendarDateTime
bool ISO8601Parser::parse_calendar_date_time()
{
// CalendarDateTime :
// DateTime Calendar[opt]
if (!parse_date_time())
return false;
(void)parse_calendar();
return true;
}
// https://tc39.es/proposal-temporal/#prod-CalendarDateTimeTimeRequired
bool ISO8601Parser::parse_calendar_date_time_time_required()
{
// CalendarDateTimeTimeRequired :
// Date TimeSpecSeparator TimeZone[opt] Calendar[opt]
StateTransaction transaction { *this };
if (!parse_date())
return false;
if (!parse_time_spec_separator())
return false;
(void)parse_time_zone();
(void)parse_calendar();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationWholeSeconds
bool ISO8601Parser::parse_duration_whole_seconds()
{
// DurationWholeSeconds :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_whole_seconds = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationSecondsFraction
bool ISO8601Parser::parse_duration_seconds_fraction()
{
// DurationSecondsFraction :
// TimeFraction
StateTransaction transaction { *this };
if (!parse_time_fraction())
return false;
m_state.parse_result.duration_seconds_fraction = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationSecondsPart
bool ISO8601Parser::parse_duration_seconds_part()
{
// DurationSecondsPart :
// DurationWholeSeconds DurationSecondsFraction[opt] SecondsDesignator
StateTransaction transaction { *this };
if (!parse_duration_whole_seconds())
return false;
(void)parse_duration_seconds_fraction();
if (!parse_seconds_designator())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationWholeMinutes
bool ISO8601Parser::parse_duration_whole_minutes()
{
// DurationWholeMinutes :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_whole_minutes = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationMinutesFraction
bool ISO8601Parser::parse_duration_minutes_fraction()
{
// DurationMinutesFraction :
// TimeFraction
StateTransaction transaction { *this };
if (!parse_time_fraction())
return false;
m_state.parse_result.duration_minutes_fraction = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationMinutesPart
bool ISO8601Parser::parse_duration_minutes_part()
{
// DurationMinutesPart :
// DurationWholeMinutes DurationMinutesFraction[opt] MinutesDesignator DurationSecondsPart[opt]
StateTransaction transaction { *this };
if (!parse_duration_whole_minutes())
return false;
(void)parse_duration_minutes_fraction();
if (!parse_minutes_designator())
return false;
(void)parse_duration_seconds_part();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationWholeHours
bool ISO8601Parser::parse_duration_whole_hours()
{
// DurationWholeHours :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_whole_hours = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationHoursFraction
bool ISO8601Parser::parse_duration_hours_fraction()
{
// DurationHoursFraction :
// TimeFraction
StateTransaction transaction { *this };
if (!parse_time_fraction())
return false;
m_state.parse_result.duration_hours_fraction = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationHoursPart
bool ISO8601Parser::parse_duration_hours_part()
{
// DurationHoursPart :
// DurationWholeHours DurationHoursFraction[opt] HoursDesignator DurationMinutesPart
// DurationWholeHours DurationHoursFraction[opt] HoursDesignator DurationSecondsPart[opt]
StateTransaction transaction { *this };
if (!parse_duration_whole_hours())
return false;
(void)parse_duration_hours_fraction();
if (!parse_hours_designator())
return false;
(void)(parse_duration_minutes_part()
|| parse_duration_seconds_part());
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationTime
bool ISO8601Parser::parse_duration_time()
{
// DurationTime :
// TimeDesignator DurationHoursPart
// TimeDesignator DurationMinutesPart
// TimeDesignator DurationSecondsPart
StateTransaction transaction { *this };
if (!parse_time_designator())
return false;
auto success = parse_duration_hours_part()
|| parse_duration_minutes_part()
|| parse_duration_seconds_part();
if (!success)
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationDays
bool ISO8601Parser::parse_duration_days()
{
// DurationDays :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_days = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationDaysPart
bool ISO8601Parser::parse_duration_days_part()
{
// DurationDaysPart :
// DurationDays DaysDesignator
StateTransaction transaction { *this };
if (!parse_duration_days())
return false;
if (!parse_days_designator())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationWeeks
bool ISO8601Parser::parse_duration_weeks()
{
// DurationWeeks :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_weeks = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationWeeksPart
bool ISO8601Parser::parse_duration_weeks_part()
{
// DurationWeeksPart :
// DurationWeeks WeeksDesignator DurationDaysPart[opt]
StateTransaction transaction { *this };
if (!parse_duration_weeks())
return false;
if (!parse_weeks_designator())
return false;
(void)parse_duration_days_part();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationMonths
bool ISO8601Parser::parse_duration_months()
{
// DurationMonths :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_months = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationMonthsPart
bool ISO8601Parser::parse_duration_months_part()
{
// DurationMonthsPart :
// DurationMonths MonthsDesignator DurationWeeksPart
// DurationMonths MonthsDesignator DurationDaysPart[opt]
StateTransaction transaction { *this };
if (!parse_duration_months())
return false;
if (!parse_months_designator())
return false;
(void)(parse_duration_weeks_part()
|| parse_duration_days_part());
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationYears
bool ISO8601Parser::parse_duration_years()
{
// DurationYears :
// DecimalDigits[~Sep]
StateTransaction transaction { *this };
if (!parse_decimal_digits())
return false;
m_state.parse_result.duration_years = transaction.parsed_string_view();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationYearsPart
bool ISO8601Parser::parse_duration_years_part()
{
// DurationYearsPart :
// DurationYears YearsDesignator DurationMonthsPart
// DurationYears YearsDesignator DurationWeeksPart
// DurationYears YearsDesignator DurationDaysPart[opt]
StateTransaction transaction { *this };
if (!parse_duration_years())
return false;
if (!parse_years_designator())
return false;
(void)(parse_duration_months_part()
|| parse_duration_weeks_part()
|| parse_duration_days_part());
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-DurationDate
bool ISO8601Parser::parse_duration_date()
{
// DurationDate :
// DurationYearsPart DurationTime[opt]
// DurationMonthsPart DurationTime[opt]
// DurationWeeksPart DurationTime[opt]
// DurationDaysPart DurationTime[opt]
auto success = parse_duration_years_part()
|| parse_duration_months_part()
|| parse_duration_weeks_part()
|| parse_duration_days_part();
if (!success)
return false;
(void)parse_duration_time();
return true;
}
// https://tc39.es/proposal-temporal/#prod-Duration
bool ISO8601Parser::parse_duration()
{
// Duration :
// Sign[opt] DurationDesignator DurationDate
// Sign[opt] DurationDesignator DurationTime
StateTransaction transaction { *this };
(void)parse_sign();
if (!parse_duration_designator())
return false;
auto success = parse_duration_date()
|| parse_duration_time();
if (!success)
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TemporalInstantString
bool ISO8601Parser::parse_temporal_instant_string()
{
// TemporalInstantString :
// Date TimeSpecSeparator[opt] TimeZoneOffsetRequired
StateTransaction transaction { *this };
if (!parse_date())
return false;
(void)parse_time_spec_separator();
if (!parse_time_zone_offset_required())
return false;
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TemporalDateTimeString
bool ISO8601Parser::parse_temporal_date_time_string()
{
// TemporalDateTimeString :
// CalendarDateTime
return parse_calendar_date_time();
}
// https://tc39.es/proposal-temporal/#prod-TemporalDurationString
bool ISO8601Parser::parse_temporal_duration_string()
{
// TemporalDurationString :
// Duration
return parse_duration();
}
// https://tc39.es/proposal-temporal/#prod-TemporalMonthDayString
bool ISO8601Parser::parse_temporal_month_day_string()
{
// TemporalMonthDayString :
// DateSpecMonthDay
// CalendarDateTime
// NOTE: Reverse order here because `DateSpecMonthDay` can be a subset of `CalendarDateTime`,
// so we'd not attempt to parse that but may not exhaust the input string.
return parse_calendar_date_time()
|| parse_date_spec_month_day();
}
// https://tc39.es/proposal-temporal/#prod-TemporalTimeString
bool ISO8601Parser::parse_temporal_time_string()
{
// TemporalTimeString :
// CalendarTime
// CalendarDateTimeTimeRequired
// NOTE: Reverse order here because `Time` can be a subset of `CalendarDateTimeTimeRequired`,
// so we'd not attempt to parse that but may not exhaust the input string.
return parse_calendar_date_time_time_required()
|| parse_calendar_time();
}
// https://tc39.es/proposal-temporal/#prod-TemporalTimeZoneIdentifier
bool ISO8601Parser::parse_temporal_time_zone_identifier()
{
// TemporalTimeZoneIdentifier :
// TimeZoneNumericUTCOffset
// TimeZoneIANAName
return parse_time_zone_numeric_utc_offset()
|| parse_time_zone_iana_name();
}
// https://tc39.es/proposal-temporal/#prod-TemporalTimeZoneString
bool ISO8601Parser::parse_temporal_time_zone_string()
{
// TemporalTimeZoneString :
// TemporalTimeZoneIdentifier
// Date TimeSpecSeparator[opt] TimeZone Calendar[opt]
StateTransaction transaction { *this };
if (!parse_temporal_time_zone_identifier()) {
if (!parse_date())
return false;
(void)parse_time_spec_separator();
if (!parse_time_zone())
return false;
(void)parse_calendar();
}
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TemporalYearMonthString
bool ISO8601Parser::parse_temporal_year_month_string()
{
// TemporalYearMonthString :
// DateSpecYearMonth
// CalendarDateTime
// NOTE: Reverse order here because `DateSpecYearMonth` can be a subset of `CalendarDateTime`,
// so we'd not attempt to parse that but may not exhaust the input string.
return parse_calendar_date_time()
|| parse_date_spec_year_month();
}
// https://tc39.es/proposal-temporal/#prod-TemporalZonedDateTimeString
bool ISO8601Parser::parse_temporal_zoned_date_time_string()
{
// TemporalZonedDateTimeString :
// Date TimeSpecSeparator[opt] TimeZoneNameRequired Calendar[opt]
StateTransaction transaction { *this };
if (!parse_date())
return false;
(void)parse_time_spec_separator();
if (!parse_time_zone_name_required())
return false;
(void)parse_calendar();
transaction.commit();
return true;
}
// https://tc39.es/proposal-temporal/#prod-TemporalCalendarString
bool ISO8601Parser::parse_temporal_calendar_string()
{
// TemporalCalendarString :
// CalendarName
// TemporalInstantString
// CalendarDateTime
// CalendarTime
// DateSpecYearMonth
// DateSpecMonthDay
return parse_calendar_name()
|| parse_temporal_instant_string()
|| parse_calendar_date_time()
|| parse_date_spec_year_month()
|| parse_date_spec_month_day()
|| parse_calendar_time();
}
}
#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS \
__JS_ENUMERATE(TemporalInstantString, parse_temporal_instant_string) \
__JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string) \
__JS_ENUMERATE(TemporalDurationString, parse_temporal_duration_string) \
__JS_ENUMERATE(TemporalMonthDayString, parse_temporal_month_day_string) \
__JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string) \
__JS_ENUMERATE(TemporalTimeZoneString, parse_temporal_time_zone_string) \
__JS_ENUMERATE(TemporalYearMonthString, parse_temporal_year_month_string) \
__JS_ENUMERATE(TemporalZonedDateTimeString, parse_temporal_zoned_date_time_string) \
__JS_ENUMERATE(TemporalCalendarString, parse_temporal_calendar_string)
Optional<ParseResult> parse_iso8601(Production production, StringView input)
{
auto parser = Detail::ISO8601Parser { input };
switch (production) {
#define __JS_ENUMERATE(ProductionName, parse_production) \
case Production::ProductionName: \
if (!parser.parse_production()) \
return {}; \
break;
JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS
#undef __JS_ENUMERATE
default:
VERIFY_NOT_REACHED();
}
// If we parsed successfully but didn't reach the end, the string doesn't match the given production.
if (!parser.lexer().is_eof())
return {};
return parser.parse_result();
}
}