From 9e9a7f710178c53c1180f65099966efb1f8fb9bb Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 19 Jan 2024 15:22:39 -0800 Subject: [PATCH 1/7] Editorial: Introduce SDOs and early errors to reject bad ISO 8601 dates Dates such as 2019-02-29, 2024-09-31, etc., can be rejected at early error time. (This changes the order in which some errors are thrown, but since they are all RangeError, it is not observable from the spec's point of view.) --- spec/abstractops.html | 59 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/spec/abstractops.html b/spec/abstractops.html index 5e83e5c05..094bd50da 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1481,8 +1481,63 @@

ISO 8601 grammar

AnnotatedDateTime[~Zoned] + +

Static Semantics: IsValidMonthDay ( ): a Boolean

+
+ + Date ::: + DateYear `-` DateMonth `-` DateDay + DateYear DateMonth DateDay + + DateSpecMonthDay ::: + `--` DateMonth `-`? DateDay + DateMonth `-`? DateDay + + + 1. If |DateDay| is *"31"* and |DateMonth| is *"02"*, *"04"*, *"06"*, *"09"*, *"11"*, return *false*. + 1. If |DateMonth| is *"02"* and |DateDay| is *"30"*, return *false*. + 1. Return *true*. + +
+ + +

Static Semantics: IsValidDate ( ): a Boolean

+
+ + Date ::: + DateYear `-` DateMonth `-` DateDay + DateYear DateMonth DateDay + + + 1. If IsValidMonthDay of |Date| is *false*, return *false*. + 1. Let _year_ be ℝ(StringToNumber(CodePointsToString(|DateYear|))). + 1. If |DateMonth| is *"02"* and |DateDay| is *"29"* and MathematicalInLeapYear(EpochTimeForYear(_year_)) = 0, return *false*. + 1. Return *true*. + +
+

Static Semantics: Early Errors

+ + Date ::: + DateYear `-` DateMonth `-` DateDay + DateYear DateMonth DateDay + + + + DateSpecMonthDay ::: + `--` DateMonth `-`? DateDay + DateMonth `-`? DateDay + + DateYear ::: TemporalSign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit @@ -1565,8 +1620,8 @@

1. Let _millisecondMV_ be 0. 1. Let _microsecondMV_ be 0. 1. Let _nanosecondMV_ be 0. - 1. If IsValidISODate(_yearMV_, _monthMV_, _dayMV_) is *false*, throw a *RangeError* exception. - 1. If IsValidTime(_hourMV_, _minuteMV_, _secondMV_, _millisecondMV_, _microsecondMV_, _nanosecondMV_) is *false*, throw a *RangeError* exception. + 1. Assert: IsValidISODate(_yearMV_, _monthMV_, _dayMV_) is *true*. + 1. Assert: IsValidTime(_hourMV_, _minuteMV_, _secondMV_, _millisecondMV_, _microsecondMV_, _nanosecondMV_) is *true*. 1. Let _timeZoneResult_ be the Record { [[Z]]: *false*, [[OffsetString]]: *undefined*, [[TimeZoneAnnotation]]: *undefined* }. 1. If _parseResult_ contains a |TimeZoneIdentifier| Parse Node, then 1. Let _identifier_ be the source text matched by the |TimeZoneIdentifier| Parse Node contained within _parseResult_. From c30c4079c16476ea861649d79dd54aff53874bed Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 19 Jan 2024 15:26:02 -0800 Subject: [PATCH 2/7] Editorial: Use early errors to simplify time-only grammar Previously the grammar relied on bizarre one-off productions such as TimeSpecWithOptionalOffsetNotAmbiguous and DateMonthWithThirtyOneDays, to prevent time strings such as 0119 (01:19 but ambiguous with 19th January) or 1524-08 (15:24 at -08:00 from UTC, but ambiguous with August of 1524) from parsing correctly without a time designator. By using early errors, these confusing productions are no longer necessary to achieve the desired result. Closes: #1984 --- polyfill/test/validStrings.mjs | 21 ++++++++++++--------- spec/abstractops.html | 27 +++++++++++++-------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index c3cd03c76..6d4cb8002 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -285,14 +285,6 @@ const timeSpec = seq( timeHour, choice([':', timeMinute, [':', timeSecond, [timeFraction]]], seq(timeMinute, [timeSecond, [timeFraction]])) ); -const timeSpecWithOptionalOffsetNotAmbiguous = withSyntaxConstraints(seq(timeSpec, [dateTimeUTCOffset]), (result) => { - if (/^(?:(?!02-?30)(?:0[1-9]|1[012])-?(?:0[1-9]|[12][0-9]|30)|(?:0[13578]|10|12)-?31)$/.test(result)) { - throw new SyntaxError('valid PlainMonthDay'); - } - if (/^(?![−-]000000)(?:[0-9]{4}|[+−-][0-9]{6})-?(?:0[1-9]|1[012])$/.test(result)) { - throw new SyntaxError('valid PlainYearMonth'); - } -}); function validateDayOfMonth(result, { year, month, day }) { if (day > ES.ISODaysInMonth(year, month)) throw SyntaxError('retry if bad day of month'); @@ -306,7 +298,18 @@ const date = withSyntaxConstraints( const dateTime = seq(date, [dateTimeSeparator, timeSpec, [dateTimeUTCOffset]]); const annotatedTime = choice( seq(timeDesignator, timeSpec, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations]), - seq(timeSpecWithOptionalOffsetNotAmbiguous, [timeZoneAnnotation], [annotations]) + seq( + withSyntaxConstraints(seq(timeSpec, [dateTimeUTCOffset]), (result) => { + if (/^(?:(?!02-?30)(?:0[1-9]|1[012])-?(?:0[1-9]|[12][0-9]|30)|(?:0[13578]|10|12)-?31)$/.test(result)) { + throw new SyntaxError('valid PlainMonthDay'); + } + if (/^(?![−-]000000)(?:[0-9]{4}|[+−-][0-9]{6})-?(?:0[1-9]|1[012])$/.test(result)) { + throw new SyntaxError('valid PlainYearMonth'); + } + }), + [timeZoneAnnotation], + [annotations] + ) ); const annotatedDateTime = seq(dateTime, [timeZoneAnnotation], [annotations]); const annotatedDateTimeTimeRequired = seq( diff --git a/spec/abstractops.html b/spec/abstractops.html index 094bd50da..010cd2884 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1239,9 +1239,6 @@

ISO 8601 grammar

`11` `12` - DateMonthWithThirtyOneDays ::: one of - `01` `03` `05` `07` `08` `10` `12` - DateDay ::: `0` NonZeroDigit `1` DecimalDigit @@ -1256,13 +1253,6 @@

ISO 8601 grammar

`--` DateMonth `-`? DateDay DateMonth `-`? DateDay - ValidMonthDay ::: - DateMonth `-`? `0` NonZeroDigit - DateMonth `-`? `1` DecimalDigit - DateMonth `-`? `2` DecimalDigit - DateMonth `-`? `30` but not one of `0230` or `02-30` - DateMonthWithThirtyOneDays `-`? `31` - Date ::: DateYear `-` DateMonth `-` DateDay DateYear DateMonth DateDay @@ -1363,16 +1353,13 @@

ISO 8601 grammar

TimeHour `:` TimeMinute `:` TimeSecond TimeFraction? TimeHour TimeMinute TimeSecond TimeFraction? - TimeSpecWithOptionalOffsetNotAmbiguous ::: - TimeSpec DateTimeUTCOffset? but not one of ValidMonthDay or DateSpecYearMonth - DateTime ::: Date Date DateTimeSeparator TimeSpec DateTimeUTCOffset? AnnotatedTime ::: TimeDesignator TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? - TimeSpecWithOptionalOffsetNotAmbiguous TimeZoneAnnotation? Annotations? + TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? AnnotatedDateTime[Zoned] ::: [~Zoned] DateTime TimeZoneAnnotation? Annotations? @@ -1518,6 +1505,18 @@

Static Semantics: IsValidDate ( ): a Boolean

Static Semantics: Early Errors

+ + AnnotatedTime ::: + TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + +
    +
  • + It is a Syntax Error if IsValidMonthDay of ParseText(|TimeSpec| |DateTimeUTCOffset|, |DateSpecMonthDay|) is *true*. +
  • +
  • + It is a Syntax Error if ParseText(|TimeSpec| |DateTimeUTCOffset|, |DateSpecYearMonth|) is a Parse Node. +
  • +
Date ::: DateYear `-` DateMonth `-` DateDay From 354a172421e3898d05db693e7cf4d95977d386bd Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 19 Jan 2024 16:19:01 -0800 Subject: [PATCH 3/7] Editorial: Use TimeSeparator[Extended] consistently in the grammar TimeSeparator[Extended] is defined in ECMA-262 and we can use it here instead of notation like `:`?. We define a new production Time which is a TimeSpec that is either fully basic or fully extended. In AnnotatedTime we keep it split out when there is no TimeDesignator present, because the early error only applies to the [~Extended] case. --- polyfill/test/validStrings.mjs | 38 ++++++++++++++++++---------------- spec/abstractops.html | 30 ++++++++++++++------------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index 6d4cb8002..a220898ce 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -76,6 +76,7 @@ class Literal { return this.str; } } +const empty = new Literal(''); class Optional { constructor(productionLike) { @@ -189,6 +190,7 @@ function seq(...productions) { // characters const temporalSign = character('+-−'); +const timeSeparator = (extended) => (extended ? character(':') : empty); const hour = zeroPaddedInclusive(0, 23, 2); const minuteSecond = zeroPaddedInclusive(0, 59, 2); const temporalDecimalSeparator = character('.,'); @@ -245,19 +247,20 @@ const timeFraction = withCode(temporalDecimalFraction, (data, result) => { function saveOffset(data, result) { data.offset = ES.ParseDateTimeUTCOffset(result); } + +const utcOffsetWithSubMinuteComponents = (extended) => + seq(temporalSign, hour, timeSeparator(extended), minuteSecond, timeSeparator(extended), minuteSecond, [ + temporalDecimalFraction + ]); +const utcOffsetMinutePrecision = seq(temporalSign, hour, [ + choice(seq(timeSeparator(true), minuteSecond), seq(timeSeparator(false), minuteSecond)) +]); const utcOffsetSubMinutePrecision = withCode( - seq( - temporalSign, - hour, - choice( - [minuteSecond, [minuteSecond, [temporalDecimalFraction]]], - seq(':', minuteSecond, [':', minuteSecond, [temporalDecimalFraction]]) - ) - ), + choice(utcOffsetMinutePrecision, utcOffsetWithSubMinuteComponents(true), utcOffsetWithSubMinuteComponents(false)), saveOffset ); const dateTimeUTCOffset = choice(utcDesignator, utcOffsetSubMinutePrecision); -const timeZoneUTCOffsetName = seq(temporalSign, hour, choice([minuteSecond], seq(':', minuteSecond))); +const timeZoneUTCOffsetName = utcOffsetMinutePrecision; const timeZoneIANAName = choice(...timezoneNames); const timeZoneIdentifier = withCode( choice(timeZoneUTCOffsetName, timeZoneIANAName), @@ -281,10 +284,9 @@ const annotations = withSyntaxConstraints(oneOrMore(choice(calendarAnnotation, a throw new SyntaxError('more than one calendar annotation and at least one critical'); } }); -const timeSpec = seq( - timeHour, - choice([':', timeMinute, [':', timeSecond, [timeFraction]]], seq(timeMinute, [timeSecond, [timeFraction]])) -); +const timeSpec = (extended) => + seq(timeHour, [timeSeparator(extended), timeMinute, [timeSeparator(extended), timeSecond, [timeFraction]]]); +const time = choice(timeSpec(true), timeSpec(false)); function validateDayOfMonth(result, { year, month, day }) { if (day > ES.ISODaysInMonth(year, month)) throw SyntaxError('retry if bad day of month'); @@ -295,11 +297,11 @@ const date = withSyntaxConstraints( choice(seq(dateYear, '-', dateMonth, '-', dateDay), seq(dateYear, dateMonth, dateDay)), validateDayOfMonth ); -const dateTime = seq(date, [dateTimeSeparator, timeSpec, [dateTimeUTCOffset]]); +const dateTime = seq(date, [dateTimeSeparator, time, [dateTimeUTCOffset]]); const annotatedTime = choice( - seq(timeDesignator, timeSpec, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations]), + seq(timeDesignator, time, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations]), seq( - withSyntaxConstraints(seq(timeSpec, [dateTimeUTCOffset]), (result) => { + withSyntaxConstraints(seq(time, [dateTimeUTCOffset]), (result) => { if (/^(?:(?!02-?30)(?:0[1-9]|1[012])-?(?:0[1-9]|[12][0-9]|30)|(?:0[13578]|10|12)-?31)$/.test(result)) { throw new SyntaxError('valid PlainMonthDay'); } @@ -315,7 +317,7 @@ const annotatedDateTime = seq(dateTime, [timeZoneAnnotation], [annotations]); const annotatedDateTimeTimeRequired = seq( date, dateTimeSeparator, - timeSpec, + time, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations] @@ -430,7 +432,7 @@ const duration = withSyntaxConstraints( } ); -const instant = seq(date, dateTimeSeparator, timeSpec, dateTimeUTCOffset, [timeZoneAnnotation], [annotations]); +const instant = seq(date, dateTimeSeparator, time, dateTimeUTCOffset, [timeZoneAnnotation], [annotations]); const zonedDateTime = seq(dateTime, timeZoneAnnotation, [annotations]); // goal elements diff --git a/spec/abstractops.html b/spec/abstractops.html index 010cd2884..5c7dcdad3 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1274,7 +1274,7 @@

ISO 8601 grammar

TemporalSign Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] MinuteSecond TemporalDecimalFraction? NormalizedUTCOffset ::: - ASCIISign Hour `:` MinuteSecond + ASCIISign Hour TimeSeparator[+Extended] MinuteSecond UTCOffsetMinutePrecision ::: TemporalSign Hour @@ -1346,27 +1346,29 @@

ISO 8601 grammar

Annotations ::: Annotation Annotations? - TimeSpec ::: + TimeSpec[Extended] ::: TimeHour - TimeHour `:` TimeMinute - TimeHour TimeMinute - TimeHour `:` TimeMinute `:` TimeSecond TimeFraction? - TimeHour TimeMinute TimeSecond TimeFraction? + TimeHour TimeSeparator[?Extended] TimeMinute + TimeHour TimeSeparator[?Extended] TimeMinute TimeSeparator[?Extended] TimeSecond TimeFraction? + + Time ::: + TimeSpec[+Extended] + TimeSpec[~Extended] DateTime ::: Date - Date DateTimeSeparator TimeSpec DateTimeUTCOffset? + Date DateTimeSeparator Time DateTimeUTCOffset? AnnotatedTime ::: - TimeDesignator TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? - TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + TimeDesignator Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? AnnotatedDateTime[Zoned] ::: [~Zoned] DateTime TimeZoneAnnotation? Annotations? [+Zoned] DateTime TimeZoneAnnotation Annotations? AnnotatedDateTimeTimeRequired ::: - Date DateTimeSeparator TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + Date DateTimeSeparator Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? AnnotatedYearMonth ::: DateSpecYearMonth TimeZoneAnnotation? Annotations? @@ -1447,7 +1449,7 @@

ISO 8601 grammar

TemporalSign? DurationDesignator DurationTime TemporalInstantString ::: - Date DateTimeSeparator TimeSpec DateTimeUTCOffset TimeZoneAnnotation? Annotations? + Date DateTimeSeparator Time DateTimeUTCOffset TimeZoneAnnotation? Annotations? TemporalDateTimeString[Zoned] ::: AnnotatedDateTime[?Zoned] @@ -1507,14 +1509,14 @@

Static Semantics: IsValidDate ( ): a Boolean

Static Semantics: Early Errors

AnnotatedTime ::: - TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations?
  • - It is a Syntax Error if IsValidMonthDay of ParseText(|TimeSpec| |DateTimeUTCOffset|, |DateSpecMonthDay|) is *true*. + It is a Syntax Error if IsValidMonthDay of ParseText(|Time| |DateTimeUTCOffset|, |DateSpecMonthDay|) is *true*.
  • - It is a Syntax Error if ParseText(|TimeSpec| |DateTimeUTCOffset|, |DateSpecYearMonth|) is a Parse Node. + It is a Syntax Error if ParseText(|Time| |DateTimeUTCOffset|, |DateSpecYearMonth|) is a Parse Node.
From 2d5f0dfd5c030ce65a3663446134ecac15f4e4da Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 19 Jan 2024 16:33:09 -0800 Subject: [PATCH 4/7] Editorial: Introduce DateSeparator[Extended] Similar to TimeSeparator[Extended] in ECMA-262, introduce DateSeparator, and Date to denote a DateSpec either fully in basic format or fully in extended format. --- polyfill/test/validStrings.mjs | 14 ++++++++--- spec/abstractops.html | 45 +++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index a220898ce..6285d7cc6 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -190,6 +190,7 @@ function seq(...productions) { // characters const temporalSign = character('+-−'); +const dateSeparator = (extended) => (extended ? character('-') : empty); const timeSeparator = (extended) => (extended ? character(':') : empty); const hour = zeroPaddedInclusive(0, 23, 2); const minuteSecond = zeroPaddedInclusive(0, 59, 2); @@ -291,12 +292,17 @@ const time = choice(timeSpec(true), timeSpec(false)); function validateDayOfMonth(result, { year, month, day }) { if (day > ES.ISODaysInMonth(year, month)) throw SyntaxError('retry if bad day of month'); } -const dateSpecMonthDay = withSyntaxConstraints(seq(['--'], dateMonth, ['-'], dateDay), validateDayOfMonth); -const dateSpecYearMonth = seq(dateYear, ['-'], dateMonth); -const date = withSyntaxConstraints( - choice(seq(dateYear, '-', dateMonth, '-', dateDay), seq(dateYear, dateMonth, dateDay)), +const dateSpecMonthDay = withSyntaxConstraints( + seq(['--'], dateMonth, choice(dateSeparator(true), dateSeparator(false)), dateDay), validateDayOfMonth ); +const dateSpecYearMonth = seq(dateYear, choice(dateSeparator(true), dateSeparator(false)), dateMonth); +const dateSpec = (extended) => + withSyntaxConstraints( + seq(dateYear, dateSeparator(extended), dateMonth, dateSeparator(extended), dateDay), + validateDayOfMonth + ); +const date = choice(dateSpec(true), dateSpec(false)); const dateTime = seq(date, [dateTimeSeparator, time, [dateTimeUTCOffset]]); const annotatedTime = choice( seq(timeDesignator, time, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations]), diff --git a/spec/abstractops.html b/spec/abstractops.html index 5c7dcdad3..1a4087766 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1191,6 +1191,10 @@

ISO 8601 grammar

`a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` + DateSeparator[Extended] ::: + [+Extended] `-` + [~Extended] [empty] + DaysDesignator ::: one of `D` `d` @@ -1247,15 +1251,19 @@

ISO 8601 grammar

`31` DateSpecYearMonth ::: - DateYear `-`? DateMonth + DateYear DateSeparator[+Extended] DateMonth + DateYear DateSeparator[~Extended] DateMonth DateSpecMonthDay ::: - `--` DateMonth `-`? DateDay - DateMonth `-`? DateDay + `--`? DateMonth DateSeparator[+Extended] DateDay + `--`? DateMonth DateSeparator[~Extended] DateDay + + DateSpec[Extended] ::: + DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay Date ::: - DateYear `-` DateMonth `-` DateDay - DateYear DateMonth DateDay + DateSpec[+Extended] + DateSpec[~Extended] TimeHour ::: Hour @@ -1474,13 +1482,12 @@

ISO 8601 grammar

Static Semantics: IsValidMonthDay ( ): a Boolean

- Date ::: - DateYear `-` DateMonth `-` DateDay - DateYear DateMonth DateDay + DateSpec[Extended] ::: + DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay DateSpecMonthDay ::: - `--` DateMonth `-`? DateDay - DateMonth `-`? DateDay + `--`? DateMonth DateSeparator[+Extended] DateDay + `--`? DateMonth DateSeparator[~Extended] DateDay 1. If |DateDay| is *"31"* and |DateMonth| is *"02"*, *"04"*, *"06"*, *"09"*, *"11"*, return *false*. @@ -1493,12 +1500,11 @@

Static Semantics: IsValidMonthDay ( ): a Boolean

Static Semantics: IsValidDate ( ): a Boolean

- Date ::: - DateYear `-` DateMonth `-` DateDay - DateYear DateMonth DateDay + DateSpec[Extended] ::: + DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay - 1. If IsValidMonthDay of |Date| is *false*, return *false*. + 1. If IsValidMonthDay of |DateSpec| is *false*, return *false*. 1. Let _year_ be ℝ(StringToNumber(CodePointsToString(|DateYear|))). 1. If |DateMonth| is *"02"* and |DateDay| is *"29"* and MathematicalInLeapYear(EpochTimeForYear(_year_)) = 0, return *false*. 1. Return *true*. @@ -1520,19 +1526,18 @@

Static Semantics: Early Errors

- Date ::: - DateYear `-` DateMonth `-` DateDay - DateYear DateMonth DateDay + DateSpec[Extended] ::: + DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay
  • - It is a Syntax Error if IsValidDate of |Date| is *false*. + It is a Syntax Error if IsValidDate of |DateSpec| is *false*.
DateSpecMonthDay ::: - `--` DateMonth `-`? DateDay - DateMonth `-`? DateDay + `--`? DateMonth DateSeparator[+Extended] DateDay + `--`? DateMonth DateSeparator[~Extended] DateDay
  • From e9ef361fb2b2f3e84fbf3829662644db6e93283d Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 19 Jan 2024 17:42:45 -0800 Subject: [PATCH 5/7] Editorial: Use grammar parameterization to express whether 'Z' is allowed In Plain types, the UTC designator 'Z' is not allowed in the string. This was previously expressed with explicit checks in the ParseTemporal... AOs, but can be more cleanly expressed with a [Z] parameter to the DateTimeUTCOffset production. --- polyfill/test/validStrings.mjs | 33 +++++++++++++++------------------ spec/abstractops.html | 33 ++++++++++++++------------------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index 6285d7cc6..58cea73c1 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -260,7 +260,7 @@ const utcOffsetSubMinutePrecision = withCode( choice(utcOffsetMinutePrecision, utcOffsetWithSubMinuteComponents(true), utcOffsetWithSubMinuteComponents(false)), saveOffset ); -const dateTimeUTCOffset = choice(utcDesignator, utcOffsetSubMinutePrecision); +const dateTimeUTCOffset = (z) => (z ? choice(utcDesignator, utcOffsetSubMinutePrecision) : utcOffsetSubMinutePrecision); const timeZoneUTCOffsetName = utcOffsetMinutePrecision; const timeZoneIANAName = choice(...timezoneNames); const timeZoneIdentifier = withCode( @@ -303,11 +303,11 @@ const dateSpec = (extended) => validateDayOfMonth ); const date = choice(dateSpec(true), dateSpec(false)); -const dateTime = seq(date, [dateTimeSeparator, time, [dateTimeUTCOffset]]); +const dateTime = (z) => seq(date, [dateTimeSeparator, time, [dateTimeUTCOffset(z)]]); const annotatedTime = choice( - seq(timeDesignator, time, [dateTimeUTCOffset], [timeZoneAnnotation], [annotations]), + seq(timeDesignator, time, [dateTimeUTCOffset(false)], [timeZoneAnnotation], [annotations]), seq( - withSyntaxConstraints(seq(time, [dateTimeUTCOffset]), (result) => { + withSyntaxConstraints(seq(time, [dateTimeUTCOffset(false)]), (result) => { if (/^(?:(?!02-?30)(?:0[1-9]|1[012])-?(?:0[1-9]|[12][0-9]|30)|(?:0[13578]|10|12)-?31)$/.test(result)) { throw new SyntaxError('valid PlainMonthDay'); } @@ -319,12 +319,13 @@ const annotatedTime = choice( [annotations] ) ); -const annotatedDateTime = seq(dateTime, [timeZoneAnnotation], [annotations]); +const annotatedDateTime = (zoned) => + seq(dateTime(zoned), zoned ? timeZoneAnnotation : [timeZoneAnnotation], [annotations]); const annotatedDateTimeTimeRequired = seq( date, dateTimeSeparator, time, - [dateTimeUTCOffset], + [dateTimeUTCOffset(false)], [timeZoneAnnotation], [annotations] ); @@ -438,19 +439,19 @@ const duration = withSyntaxConstraints( } ); -const instant = seq(date, dateTimeSeparator, time, dateTimeUTCOffset, [timeZoneAnnotation], [annotations]); -const zonedDateTime = seq(dateTime, timeZoneAnnotation, [annotations]); +const instant = seq(date, dateTimeSeparator, time, dateTimeUTCOffset(true), [timeZoneAnnotation], [annotations]); +const zonedDateTime = annotatedDateTime(true); // goal elements const goals = { Instant: instant, - Date: annotatedDateTime, - DateTime: annotatedDateTime, + Date: annotatedDateTime(false), + DateTime: annotatedDateTime(false), Duration: duration, - MonthDay: choice(annotatedMonthDay, annotatedDateTime), + MonthDay: choice(annotatedMonthDay, annotatedDateTime(false)), Time: choice(annotatedTime, annotatedDateTimeTimeRequired), TimeZone: choice(timeZoneIdentifier, zonedDateTime, instant), - YearMonth: choice(annotatedYearMonth, annotatedDateTime), + YearMonth: choice(annotatedYearMonth, annotatedDateTime(false)), ZonedDateTime: zonedDateTime }; @@ -478,16 +479,12 @@ const comparisonItems = { YearMonth: ['year', 'month', 'calendar'], ZonedDateTime: [...dateItems, ...timeItems, 'offset', 'z', 'tzAnnotation', 'calendar'] }; -const plainModes = ['Date', 'DateTime', 'MonthDay', 'Time', 'YearMonth']; function fuzzMode(mode) { console.log('// starting to fuzz ' + mode); for (let count = 0; count < 1000; count++) { - let generatedData, fuzzed; - do { - generatedData = {}; - fuzzed = goals[mode].generate(generatedData); - } while (plainModes.includes(mode) && /[0-9][zZ]/.test(fuzzed)); + const generatedData = {}; + const fuzzed = goals[mode].generate(generatedData); try { const parsingMethod = ES[`ParseTemporal${mode}StringRaw`] ?? ES[`ParseTemporal${mode}String`]; const parsed = parsingMethod(fuzzed); diff --git a/spec/abstractops.html b/spec/abstractops.html index 1a4087766..7f145c0e9 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1294,8 +1294,8 @@

    ISO 8601 grammar

    UTCOffsetWithSubMinuteComponents[+Extended] UTCOffsetWithSubMinuteComponents[~Extended] - DateTimeUTCOffset ::: - UTCDesignator + DateTimeUTCOffset[Z] ::: + [+Z] UTCDesignator UTCOffsetSubMinutePrecision TimeZoneUTCOffsetName ::: @@ -1363,20 +1363,20 @@

    ISO 8601 grammar

    TimeSpec[+Extended] TimeSpec[~Extended] - DateTime ::: + DateTime[Z] ::: Date - Date DateTimeSeparator Time DateTimeUTCOffset? + Date DateTimeSeparator Time DateTimeUTCOffset[?Z]? AnnotatedTime ::: - TimeDesignator Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? - Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + TimeDesignator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? + Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? AnnotatedDateTime[Zoned] ::: - [~Zoned] DateTime TimeZoneAnnotation? Annotations? - [+Zoned] DateTime TimeZoneAnnotation Annotations? + [~Zoned] DateTime[~Z] TimeZoneAnnotation? Annotations? + [+Zoned] DateTime[+Z] TimeZoneAnnotation Annotations? AnnotatedDateTimeTimeRequired ::: - Date DateTimeSeparator Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + Date DateTimeSeparator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? AnnotatedYearMonth ::: DateSpecYearMonth TimeZoneAnnotation? Annotations? @@ -1457,7 +1457,7 @@

    ISO 8601 grammar

    TemporalSign? DurationDesignator DurationTime TemporalInstantString ::: - Date DateTimeSeparator Time DateTimeUTCOffset TimeZoneAnnotation? Annotations? + Date DateTimeSeparator Time DateTimeUTCOffset[+Z] TimeZoneAnnotation? Annotations? TemporalDateTimeString[Zoned] ::: AnnotatedDateTime[?Zoned] @@ -1515,14 +1515,14 @@

    Static Semantics: IsValidDate ( ): a Boolean

    Static Semantics: Early Errors

    AnnotatedTime ::: - Time DateTimeUTCOffset? TimeZoneAnnotation? Annotations? + Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations?
    • - It is a Syntax Error if IsValidMonthDay of ParseText(|Time| |DateTimeUTCOffset|, |DateSpecMonthDay|) is *true*. + It is a Syntax Error if IsValidMonthDay of ParseText(|Time| |DateTimeUTCOffset[~Z]|, |DateSpecMonthDay|) is *true*.
    • - It is a Syntax Error if ParseText(|Time| |DateTimeUTCOffset|, |DateSpecYearMonth|) is a Parse Node. + It is a Syntax Error if ParseText(|Time| |DateTimeUTCOffset[~Z]|, |DateSpecYearMonth|) is a Parse Node.
    @@ -1754,9 +1754,8 @@

    It parses the argument as an ISO 8601 string and returns a Record representing each date and time component needed to construct a Temporal.PlainDateTime instance as a distinct field.
    - 1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalDateTimeString|). + 1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalDateTimeString[~Zoned]|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. - 1. If _parseResult_ contains a |UTCDesignator| Parse Node, throw a *RangeError* exception. 1. Return ? ParseISODateTime(_isoString_). @@ -1838,7 +1837,6 @@

    1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalMonthDayString|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. - 1. If _parseResult_ contains a |UTCDesignator| Parse Node, throw a *RangeError* exception. 1. Let _result_ be ? ParseISODateTime(_isoString_). 1. Let _year_ be _result_.[[Year]]. 1. If _parseResult_ does not contain a |DateYear| Parse Node, then @@ -1865,7 +1863,6 @@

    1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalDateTimeString|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. - 1. If _parseResult_ contains a |UTCDesignator| ParseNode but no |TimeZoneAnnotation| Parse Node, throw a *RangeError* exception. 1. Return ? ParseISODateTime(_isoString_). @@ -1883,7 +1880,6 @@

    1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalTimeString|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. - 1. If _parseResult_ contains a |UTCDesignator| Parse Node, throw a *RangeError* exception. 1. Let _result_ be ? ParseISODateTime(_isoString_). 1. Return the Record { [[Hour]]: _result_.[[Hour]], @@ -1950,7 +1946,6 @@

    1. Let _parseResult_ be ParseText(StringToCodePoints(_isoString_), |TemporalYearMonthString|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. - 1. If _parseResult_ contains a |UTCDesignator| Parse Node, throw a *RangeError* exception. 1. Let _result_ be ? ParseISODateTime(_isoString_). 1. Return the Record { [[Year]]: _result_.[[Year]], From d358e03d32bcdb39ab9f85c5ca3da86432c9725e Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 23 Jan 2024 16:52:58 -0800 Subject: [PATCH 6/7] Editorial: Replace AnnotatedDateTimeTimeRequired production with parameterization Another place where we can deduplicate parts of the grammar. --- polyfill/test/validStrings.mjs | 32 +++++++++++++++----------------- spec/abstractops.html | 21 +++++++++------------ 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index 58cea73c1..f49449902 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -303,7 +303,13 @@ const dateSpec = (extended) => validateDayOfMonth ); const date = choice(dateSpec(true), dateSpec(false)); -const dateTime = (z) => seq(date, [dateTimeSeparator, time, [dateTimeUTCOffset(z)]]); +const dateTime = (z, timeRequired) => + seq( + date, + timeRequired + ? seq(dateTimeSeparator, time, [dateTimeUTCOffset(z)]) + : [dateTimeSeparator, time, [dateTimeUTCOffset(z)]] + ); const annotatedTime = choice( seq(timeDesignator, time, [dateTimeUTCOffset(false)], [timeZoneAnnotation], [annotations]), seq( @@ -319,16 +325,8 @@ const annotatedTime = choice( [annotations] ) ); -const annotatedDateTime = (zoned) => - seq(dateTime(zoned), zoned ? timeZoneAnnotation : [timeZoneAnnotation], [annotations]); -const annotatedDateTimeTimeRequired = seq( - date, - dateTimeSeparator, - time, - [dateTimeUTCOffset(false)], - [timeZoneAnnotation], - [annotations] -); +const annotatedDateTime = (zoned, timeRequired) => + seq(dateTime(zoned, timeRequired), zoned ? timeZoneAnnotation : [timeZoneAnnotation], [annotations]); const annotatedYearMonth = withSyntaxConstraints( seq(dateSpecYearMonth, [timeZoneAnnotation], [annotations]), (result, data) => { @@ -440,18 +438,18 @@ const duration = withSyntaxConstraints( ); const instant = seq(date, dateTimeSeparator, time, dateTimeUTCOffset(true), [timeZoneAnnotation], [annotations]); -const zonedDateTime = annotatedDateTime(true); +const zonedDateTime = annotatedDateTime(true, false); // goal elements const goals = { Instant: instant, - Date: annotatedDateTime(false), - DateTime: annotatedDateTime(false), + Date: annotatedDateTime(false, false), + DateTime: annotatedDateTime(false, false), Duration: duration, - MonthDay: choice(annotatedMonthDay, annotatedDateTime(false)), - Time: choice(annotatedTime, annotatedDateTimeTimeRequired), + MonthDay: choice(annotatedMonthDay, annotatedDateTime(false, false)), + Time: choice(annotatedTime, annotatedDateTime(false, true)), TimeZone: choice(timeZoneIdentifier, zonedDateTime, instant), - YearMonth: choice(annotatedYearMonth, annotatedDateTime(false)), + YearMonth: choice(annotatedYearMonth, annotatedDateTime(false, false)), ZonedDateTime: zonedDateTime }; diff --git a/spec/abstractops.html b/spec/abstractops.html index 7f145c0e9..511b76406 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1363,20 +1363,17 @@

    ISO 8601 grammar

    TimeSpec[+Extended] TimeSpec[~Extended] - DateTime[Z] ::: - Date + DateTime[Z, TimeRequired] ::: + [~TimeRequired] Date Date DateTimeSeparator Time DateTimeUTCOffset[?Z]? AnnotatedTime ::: TimeDesignator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? - AnnotatedDateTime[Zoned] ::: - [~Zoned] DateTime[~Z] TimeZoneAnnotation? Annotations? - [+Zoned] DateTime[+Z] TimeZoneAnnotation Annotations? - - AnnotatedDateTimeTimeRequired ::: - Date DateTimeSeparator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? + AnnotatedDateTime[Zoned, TimeRequired] ::: + [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations? + [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations? AnnotatedYearMonth ::: DateSpecYearMonth TimeZoneAnnotation? Annotations? @@ -1460,22 +1457,22 @@

    ISO 8601 grammar

    Date DateTimeSeparator Time DateTimeUTCOffset[+Z] TimeZoneAnnotation? Annotations? TemporalDateTimeString[Zoned] ::: - AnnotatedDateTime[?Zoned] + AnnotatedDateTime[?Zoned, ~TimeRequired] TemporalDurationString ::: Duration TemporalMonthDayString ::: AnnotatedMonthDay - AnnotatedDateTime[~Zoned] + AnnotatedDateTime[~Zoned, ~TimeRequired] TemporalTimeString ::: AnnotatedTime - AnnotatedDateTimeTimeRequired + AnnotatedDateTime[~Zoned, +TimeRequired] TemporalYearMonthString ::: AnnotatedYearMonth - AnnotatedDateTime[~Zoned] + AnnotatedDateTime[~Zoned, ~TimeRequired] From c507068d681107bf155ae9353e72f5a4e8439316 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 23 Jan 2024 18:04:08 -0800 Subject: [PATCH 7/7] Editorial: Parameterize UTCOffset grammar Instead of two separate productions UTCOffsetSubMinutePrecision and UTCOffsetMinutePrecision, we can have one UTCOffset production with a SubMinutePrecision parameter. --- polyfill/test/validStrings.mjs | 31 ++++++++++++++++++------------- spec/abstractops.html | 20 +++++++------------- spec/intl.html | 2 +- spec/mainadditions.html | 6 +++--- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index f49449902..7c5f1f6d9 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -245,23 +245,28 @@ const timeFraction = withCode(temporalDecimalFraction, (data, result) => { data.microsecond = +fraction.slice(3, 6); data.nanosecond = +fraction.slice(6, 9); }); + function saveOffset(data, result) { data.offset = ES.ParseDateTimeUTCOffset(result); } - -const utcOffsetWithSubMinuteComponents = (extended) => - seq(temporalSign, hour, timeSeparator(extended), minuteSecond, timeSeparator(extended), minuteSecond, [ - temporalDecimalFraction +const utcOffset = (subMinutePrecision) => + seq(temporalSign, hour, [ + choice( + seq( + timeSeparator(true), + minuteSecond, + subMinutePrecision ? [timeSeparator(true), minuteSecond, [temporalDecimalFraction]] : empty + ), + seq( + timeSeparator(false), + minuteSecond, + subMinutePrecision ? [timeSeparator(false), minuteSecond, [temporalDecimalFraction]] : empty + ) + ) ]); -const utcOffsetMinutePrecision = seq(temporalSign, hour, [ - choice(seq(timeSeparator(true), minuteSecond), seq(timeSeparator(false), minuteSecond)) -]); -const utcOffsetSubMinutePrecision = withCode( - choice(utcOffsetMinutePrecision, utcOffsetWithSubMinuteComponents(true), utcOffsetWithSubMinuteComponents(false)), - saveOffset -); -const dateTimeUTCOffset = (z) => (z ? choice(utcDesignator, utcOffsetSubMinutePrecision) : utcOffsetSubMinutePrecision); -const timeZoneUTCOffsetName = utcOffsetMinutePrecision; +const dateTimeUTCOffset = (z) => + z ? choice(utcDesignator, withCode(utcOffset(true), saveOffset)) : withCode(utcOffset(true), saveOffset); +const timeZoneUTCOffsetName = utcOffset(false); const timeZoneIANAName = choice(...timezoneNames); const timeZoneIdentifier = withCode( choice(timeZoneUTCOffsetName, timeZoneIANAName), diff --git a/spec/abstractops.html b/spec/abstractops.html index 511b76406..686e2ad13 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1278,28 +1278,22 @@

    ISO 8601 grammar

    TimeFraction ::: TemporalDecimalFraction - UTCOffsetWithSubMinuteComponents[Extended] ::: - TemporalSign Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] MinuteSecond TemporalDecimalFraction? - NormalizedUTCOffset ::: ASCIISign Hour TimeSeparator[+Extended] MinuteSecond - UTCOffsetMinutePrecision ::: + UTCOffset[SubMinutePrecision] ::: TemporalSign Hour TemporalSign Hour TimeSeparator[+Extended] MinuteSecond TemporalSign Hour TimeSeparator[~Extended] MinuteSecond - - UTCOffsetSubMinutePrecision ::: - UTCOffsetMinutePrecision - UTCOffsetWithSubMinuteComponents[+Extended] - UTCOffsetWithSubMinuteComponents[~Extended] + [+SubMinutePrecision] TemporalSign Hour TimeSeparator[+Extended] MinuteSecond TimeSeparator[+Extended] MinuteSecond TemporalDecimalFraction? + [+SubMinutePrecision] TemporalSign Hour TimeSeparator[~Extended] MinuteSecond TimeSeparator[~Extended] MinuteSecond TemporalDecimalFraction? DateTimeUTCOffset[Z] ::: [+Z] UTCDesignator - UTCOffsetSubMinutePrecision + UTCOffset[+SubMinutePrecision] TimeZoneUTCOffsetName ::: - UTCOffsetMinutePrecision + UTCOffset[~SubMinutePrecision] TZLeadingChar ::: Alpha @@ -1631,8 +1625,8 @@

    1. Set _timeZoneResult_.[[TimeZoneAnnotation]] to CodePointsToString(_identifier_). 1. If _parseResult_ contains a |UTCDesignator| Parse Node, then 1. Set _timeZoneResult_.[[Z]] to *true*. - 1. Else if _parseResult_ contains a |UTCOffsetSubMinutePrecision| Parse Node, then - 1. Let _offset_ be the source text matched by the |UTCOffsetSubMinutePrecision| Parse Node contained within _parseResult_. + 1. Else if _parseResult_ contains a |UTCOffset[+SubMinutePrecision]| Parse Node, then + 1. Let _offset_ be the source text matched by the |UTCOffset[+SubMinutePrecision]| Parse Node contained within _parseResult_. 1. Set _timeZoneResult_.[[OffsetString]] to CodePointsToString(_offset_). 1. Return the Record { [[Year]]: _yearMV_, diff --git a/spec/intl.html b/spec/intl.html index ba25a18dd..1650b79e0 100644 --- a/spec/intl.html +++ b/spec/intl.html @@ -650,7 +650,7 @@

    1. If _toLocaleStringTimeZone_ is present, throw a *TypeError* exception. 1. Set _timeZone_ to ? ToString(_timeZone_). 1. If IsTimeZoneOffsetString(_timeZone_) is *true*, then - 1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZone_), |UTCOffset||UTCOffsetMinutePrecision|). + 1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZone_), |UTCOffset||UTCOffset[~SubMinutePrecision]|). 1. Assert: _parseResult_ is a Parse Node. 1. If _parseResult_ contains more than one |MinuteSecond| Parse Node, throw a *RangeError* exception. 1. Let _offsetNanoseconds_ be ParseTimeZoneOffsetString? ParseDateTimeUTCOffset(_timeZone_). diff --git a/spec/mainadditions.html b/spec/mainadditions.html index 977ce9b0a..5f9f1d28d 100644 --- a/spec/mainadditions.html +++ b/spec/mainadditions.html @@ -252,8 +252,8 @@

    Time Zone Offset String FormatFormats

    ECMAScript defines string interchange formats for UTC offsets, derived from ISO 8601. - UTC offsets that represent offset time zone identifiers, or that are intended for interoperability with ISO 8601, use only hours and minutes and are specified by |UTCOffsetMinutePrecision|. - UTC offsets that represent the offset of a named or custom time zone can be more precise, and are specified by |UTCOffsetSubMinutePrecision|. + UTC offsets that represent offset time zone identifiers, or that are intended for interoperability with ISO 8601, use only hours and minutes and are specified by |UTCOffset[~SubMinutePrecision]|. + UTC offsets that represent the offset of a named or custom time zone can be more precise, and are specified by |UTCOffset[+SubMinutePrecision]|.

    These formats are described by the ISO String grammar in . @@ -461,7 +461,7 @@

    - 1. Let _parseResult_ be ParseText(StringToCodePoints(_offsetString_), |UTCOffset||UTCOffsetSubMinutePrecision|). + 1. Let _parseResult_ be ParseText(StringToCodePoints(_offsetString_), |UTCOffset||UTCOffset[+SubMinutePrecision]|). 1. Assert: _parseResult_ is not a List of errors. 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. 1. Assert: _parseResult_ contains a |TemporalSign| Parse Node.