1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "builtin/temporal/TemporalParser.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/Range.h"
13 #include "mozilla/Result.h"
14 #include "mozilla/Span.h"
15 #include "mozilla/TextUtils.h"
19 #include <initializer_list>
23 #include <string_view>
24 #include <type_traits>
28 #include "NamespaceImports.h"
30 #include "builtin/temporal/Duration.h"
31 #include "builtin/temporal/PlainDate.h"
32 #include "builtin/temporal/PlainTime.h"
33 #include "builtin/temporal/TemporalTypes.h"
34 #include "builtin/temporal/TemporalUnit.h"
35 #include "gc/Barrier.h"
36 #include "gc/Tracer.h"
37 #include "js/ErrorReport.h"
38 #include "js/friend/ErrorMessages.h"
40 #include "js/RootingAPI.h"
41 #include "js/TypeDecls.h"
42 #include "util/Text.h"
43 #include "vm/JSAtomState.h"
44 #include "vm/JSContext.h"
45 #include "vm/StringType.h"
48 using namespace js::temporal
;
50 // TODO: Better error message for empty strings?
51 // TODO: Add string input to error message?
52 // TODO: Better error messages, for example display current character?
53 // https://bugzilla.mozilla.org/show_bug.cgi?id=1839676
55 struct StringName final
{
56 // Start position and length of this name.
60 bool present() const { return length
> 0; }
63 static JSLinearString
* ToString(JSContext
* cx
, JSString
* string
,
64 const StringName
& name
) {
65 MOZ_ASSERT(name
.present());
66 return NewDependentString(cx
, string
, name
.start
, name
.length
);
69 template <typename CharT
>
70 bool EqualCharIgnoreCaseAscii(CharT c1
, char c2
) {
71 if constexpr (sizeof(CharT
) > sizeof(char)) {
72 if (!mozilla::IsAscii(c1
)) {
77 static constexpr auto toLower
= 0x20;
78 static_assert('a' - 'A' == toLower
);
80 // Convert both characters to lower case before the comparison.
82 if (mozilla::IsAsciiUppercaseAlpha(c1
)) {
86 if (mozilla::IsAsciiUppercaseAlpha(c2
)) {
92 using CalendarName
= StringName
;
93 using AnnotationKey
= StringName
;
94 using AnnotationValue
= StringName
;
95 using TimeZoneName
= StringName
;
97 struct Annotation final
{
99 AnnotationValue value
;
100 bool critical
= false;
103 struct TimeSpec final
{
107 struct TimeZoneUTCOffset final
{
108 // ±1 for time zones with an offset, otherwise 0.
111 // An integer in the range [0, 23].
114 // An integer in the range [0, 59].
118 struct DateTimeUTCOffset final
{
119 // ±1 for time zones with an offset, otherwise 0.
122 // An integer in the range [0, 23].
125 // An integer in the range [0, 59].
128 // An integer in the range [0, 59].
131 // An integer in the range [0, 999'999].
132 int32_t fractionalPart
= 0;
134 // Time zone with sub-minute precision.
135 bool subMinutePrecision
= false;
137 // Convert to a TimeZoneUTCOffset.
138 TimeZoneUTCOffset
toTimeZoneUTCOffset() const {
139 MOZ_ASSERT(!subMinutePrecision
, "unexpected sub-minute precision");
140 return {sign
, hour
, minute
};
145 * ParseDateTimeUTCOffset ( offsetString )
147 static int64_t ParseDateTimeUTCOffset(const DateTimeUTCOffset
& offset
) {
148 constexpr int64_t nanoPerSec
= 1'000'000'000;
150 MOZ_ASSERT(offset
.sign
== -1 || offset
.sign
== +1);
151 MOZ_ASSERT(0 <= offset
.hour
&& offset
.hour
< 24);
152 MOZ_ASSERT(0 <= offset
.minute
&& offset
.minute
< 60);
153 MOZ_ASSERT(0 <= offset
.second
&& offset
.second
< 60);
154 MOZ_ASSERT(0 <= offset
.fractionalPart
&& offset
.fractionalPart
< nanoPerSec
);
156 // sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds).
157 int64_t seconds
= (offset
.hour
* 60 + offset
.minute
) * 60 + offset
.second
;
158 int64_t nanos
= (seconds
* nanoPerSec
) + offset
.fractionalPart
;
159 int64_t result
= offset
.sign
* nanos
;
161 MOZ_ASSERT(std::abs(result
) < ToNanoseconds(TemporalUnit::Day
),
162 "time zone offset is less than 24:00 hours");
167 static int32_t ParseTimeZoneOffset(const TimeZoneUTCOffset
& offset
) {
168 MOZ_ASSERT(offset
.sign
== -1 || offset
.sign
== +1);
169 MOZ_ASSERT(0 <= offset
.hour
&& offset
.hour
< 24);
170 MOZ_ASSERT(0 <= offset
.minute
&& offset
.minute
< 60);
172 // sign × (hour × 60 + minute).
173 int32_t result
= offset
.sign
* (offset
.hour
* 60 + offset
.minute
);
175 MOZ_ASSERT(std::abs(result
) < UnitsPerDay(TemporalUnit::Minute
),
176 "time zone offset is less than 24:00 hours");
182 * Struct to hold time zone annotations.
184 struct TimeZoneAnnotation final
{
186 TimeZoneUTCOffset offset
;
192 * Returns true iff the time zone has an offset part, e.g. "+01:00".
194 bool hasOffset() const { return offset
.sign
!= 0; }
197 * Returns true iff the time zone has an IANA name, e.g. "Asia/Tokyo".
199 bool hasName() const { return name
.present(); }
203 * Struct to hold any time zone parts of a parsed string.
205 struct TimeZoneString final
{
206 // Date-time UTC offset.
207 DateTimeUTCOffset offset
;
209 // Time zone annotation;
210 TimeZoneAnnotation annotation
;
215 static auto from(DateTimeUTCOffset offset
) {
216 TimeZoneString timeZone
{};
217 timeZone
.offset
= offset
;
221 static auto from(TimeZoneUTCOffset offset
) {
222 TimeZoneString timeZone
{};
223 timeZone
.annotation
.offset
= offset
;
227 static auto from(TimeZoneName name
) {
228 TimeZoneString timeZone
{};
229 timeZone
.annotation
.name
= name
;
234 TimeZoneString timeZone
{};
240 * Returns true iff the time zone has an offset part, e.g. "+01:00".
242 bool hasOffset() const { return offset
.sign
!= 0; }
245 * Returns true iff the time zone has an annotation.
247 bool hasAnnotation() const {
248 return annotation
.hasName() || annotation
.hasOffset();
252 * Returns true iff the time zone uses the "Z" abbrevation to denote UTC time.
254 bool isUTC() const { return utc
; }
258 * Struct to hold the parsed date, time, time zone, and calendar components.
260 struct ZonedDateTimeString final
{
263 TimeZoneString timeZone
;
264 CalendarName calendar
;
267 template <typename CharT
>
268 static bool IsISO8601Calendar(mozilla::Span
<const CharT
> calendar
) {
269 static constexpr std::string_view iso8601
= "iso8601";
271 if (calendar
.size() != iso8601
.length()) {
275 for (size_t i
= 0; i
< iso8601
.length(); i
++) {
276 if (!EqualCharIgnoreCaseAscii(calendar
[i
], iso8601
[i
])) {
283 static constexpr int32_t AbsentYear
= INT32_MAX
;
286 * ParseISODateTime ( isoString )
288 static bool ParseISODateTime(JSContext
* cx
, const ZonedDateTimeString
& parsed
,
289 PlainDateTime
* result
) {
290 // Steps 1-6, 8, 10-13 (Not applicable here).
292 PlainDateTime dateTime
= {parsed
.date
, parsed
.time
};
294 // NOTE: ToIntegerOrInfinity("") is 0.
295 if (dateTime
.date
.year
== AbsentYear
) {
296 dateTime
.date
.year
= 0;
300 if (dateTime
.date
.month
== 0) {
301 dateTime
.date
.month
= 1;
305 if (dateTime
.date
.day
== 0) {
306 dateTime
.date
.day
= 1;
310 if (dateTime
.time
.second
== 60) {
311 dateTime
.time
.second
= 59;
314 // ParseISODateTime, steps 15-16 (Not applicable in our implementation).
316 // Call ThrowIfInvalidISODate to report an error if |days| exceeds the number
317 // of days in the month. All other values are already in-bounds.
318 MOZ_ASSERT(std::abs(dateTime
.date
.year
) <= 999'999);
319 MOZ_ASSERT(1 <= dateTime
.date
.month
&& dateTime
.date
.month
<= 12);
320 MOZ_ASSERT(1 <= dateTime
.date
.day
&& dateTime
.date
.day
<= 31);
322 // ParseISODateTime, step 17.
323 if (!ThrowIfInvalidISODate(cx
, dateTime
.date
)) {
327 // ParseISODateTime, step 18.
328 MOZ_ASSERT(IsValidTime(dateTime
.time
));
330 // Steps 19-25. (Handled in caller.)
336 static bool ParseTimeZoneAnnotation(JSContext
* cx
,
337 const TimeZoneAnnotation
& annotation
,
338 JSLinearString
* linear
,
339 MutableHandle
<ParsedTimeZone
> result
) {
340 MOZ_ASSERT(annotation
.hasOffset() || annotation
.hasName());
342 if (annotation
.hasOffset()) {
343 int32_t offset
= ParseTimeZoneOffset(annotation
.offset
);
344 result
.set(ParsedTimeZone::fromOffset(offset
));
348 auto* str
= ToString(cx
, linear
, annotation
.name
);
352 result
.set(ParsedTimeZone::fromName(str
));
357 * Struct for the parsed duration components.
359 struct TemporalDurationString final
{
360 // A non-negative integer or +Infinity.
363 // A non-negative integer or +Infinity.
366 // A non-negative integer or +Infinity.
369 // A non-negative integer or +Infinity.
372 // A non-negative integer or +Infinity.
375 // A non-negative integer or +Infinity.
378 // A non-negative integer or +Infinity.
381 // An integer in the range [0, 999'999].
382 int32_t hoursFraction
= 0;
384 // An integer in the range [0, 999'999].
385 int32_t minutesFraction
= 0;
387 // An integer in the range [0, 999'999].
388 int32_t secondsFraction
= 0;
390 // ±1 when an offset is present, otherwise 0.
394 class ParserError final
{
395 JSErrNum error_
= JSMSG_NOT_AN_ERROR
;
398 constexpr MOZ_IMPLICIT
ParserError(JSErrNum error
) : error_(error
) {}
400 constexpr JSErrNum
error() const { return error_
; }
402 constexpr operator JSErrNum() const { return error(); }
405 namespace mozilla::detail
{
406 // Zero is used for tagging, so it mustn't be an error.
407 static_assert(static_cast<JSErrNum
>(0) == JSMSG_NOT_AN_ERROR
);
409 // Ensure efficient packing of the error type.
411 struct UnusedZero
<::ParserError
> {
413 using Error
= ::ParserError
;
414 using ErrorKind
= JSErrNum
;
417 using StorageType
= std::underlying_type_t
<ErrorKind
>;
419 static constexpr bool value
= true;
420 static constexpr StorageType nullValue
= 0;
422 static constexpr Error
Inspect(const StorageType
& aValue
) {
423 return Error(static_cast<ErrorKind
>(aValue
));
425 static constexpr Error
Unwrap(StorageType aValue
) {
426 return Error(static_cast<ErrorKind
>(aValue
));
428 static constexpr StorageType
Store(Error aValue
) {
429 return static_cast<StorageType
>(aValue
.error());
432 } // namespace mozilla::detail
434 static_assert(mozilla::Result
<ZonedDateTimeString
, ParserError
>::Strategy
!=
435 mozilla::detail::PackingStrategy::Variant
);
437 template <typename CharT
>
438 class StringReader final
{
439 mozilla::Span
<const CharT
> string_
;
441 // Current position in the string.
445 explicit StringReader(mozilla::Span
<const CharT
> string
) : string_(string
) {}
448 * Returns the input string.
450 mozilla::Span
<const CharT
> string() const { return string_
; }
453 * Returns a substring of the input string.
455 mozilla::Span
<const CharT
> substring(const StringName
& name
) const {
456 MOZ_ASSERT(name
.present());
457 return string_
.Subspan(name
.start
, name
.length
);
461 * Returns the current parse position.
463 size_t index() const { return index_
; }
466 * Returns the length of the input string-
468 size_t length() const { return string_
.size(); }
471 * Returns true iff the whole string has been parsed.
473 bool atEnd() const { return index() == length(); }
476 * Reset the parser to a previous parse position.
478 void reset(size_t index
= 0) {
479 MOZ_ASSERT(index
<= length());
484 * Returns true if at least `amount` characters can be read from the current
487 bool hasMore(size_t amount
) const { return index() + amount
<= length(); }
490 * Advances the parse position by `amount` characters.
492 void advance(size_t amount
) {
493 MOZ_ASSERT(hasMore(amount
));
498 * Returns the character at the current parse position.
500 CharT
current() const { return string()[index()]; }
503 * Returns the character at the next parse position.
505 CharT
next() const { return string()[index() + 1]; }
508 * Returns the character at position `index`.
510 CharT
at(size_t index
) const { return string()[index
]; }
513 template <typename CharT
>
514 class TemporalParser final
{
515 StringReader
<CharT
> reader_
;
518 * Read an unlimited amount of decimal digits, returning `Nothing` if no
521 mozilla::Maybe
<double> digits(JSContext
* cx
);
524 * Read exactly `length` digits, returning `Nothing` on failure.
526 mozilla::Maybe
<int32_t> digits(size_t length
) {
527 MOZ_ASSERT(length
> 0, "can't read zero digits");
528 MOZ_ASSERT(length
<= std::numeric_limits
<int32_t>::digits10
,
529 "can't read more than digits10 digits without overflow");
531 if (!reader_
.hasMore(length
)) {
532 return mozilla::Nothing();
535 size_t index
= reader_
.index();
536 for (size_t i
= 0; i
< length
; i
++) {
537 auto ch
= reader_
.at(index
+ i
);
538 if (!mozilla::IsAsciiDigit(ch
)) {
539 return mozilla::Nothing();
541 num
= num
* 10 + AsciiDigitToNumber(ch
);
543 reader_
.advance(length
);
544 return mozilla::Some(num
);
547 // TimeFractionalPart :
551 // DecimalSeparator TimeFractionalPart
552 mozilla::Maybe
<int32_t> fraction() {
553 if (!reader_
.hasMore(2)) {
554 return mozilla::Nothing();
556 if (!hasDecimalSeparator() || !mozilla::IsAsciiDigit(reader_
.next())) {
557 return mozilla::Nothing();
560 // Consume the decimal separator.
561 MOZ_ALWAYS_TRUE(decimalSeparator());
563 // Maximal nine fractional digits are supported.
564 constexpr size_t maxFractions
= 9;
566 // Read up to |maxFractions| digits.
568 size_t index
= reader_
.index();
570 for (; i
< std::min(reader_
.length() - index
, maxFractions
); i
++) {
571 CharT ch
= reader_
.at(index
+ i
);
572 if (!mozilla::IsAsciiDigit(ch
)) {
575 num
= num
* 10 + AsciiDigitToNumber(ch
);
578 // Skip past the read digits.
581 // Normalize the fraction to |maxFractions| digits.
582 for (; i
< maxFractions
; i
++) {
585 return mozilla::Some(num
);
589 * Returns true iff the current character is `ch`.
591 bool hasCharacter(CharT ch
) const {
592 return reader_
.hasMore(1) && reader_
.current() == ch
;
596 * Consumes the current character if it's equal to `ch` and then returns
597 * `true`. Otherwise returns `false`.
599 bool character(CharT ch
) {
600 if (!hasCharacter(ch
)) {
608 * Consumes the next characters if they're equal to `str` and then returns
609 * `true`. Otherwise returns `false`.
612 bool string(const char (&str
)[N
]) {
613 static_assert(N
> 2, "use character() for one element strings");
615 if (!reader_
.hasMore(N
- 1)) {
618 size_t index
= reader_
.index();
619 for (size_t i
= 0; i
< N
- 1; i
++) {
620 if (reader_
.at(index
+ i
) != str
[i
]) {
624 reader_
.advance(N
- 1);
629 * Returns true if the next two characters are ASCII alphabetic characters.
631 bool hasTwoAsciiAlpha() {
632 if (!reader_
.hasMore(2)) {
635 size_t index
= reader_
.index();
636 return mozilla::IsAsciiAlpha(reader_
.at(index
)) &&
637 mozilla::IsAsciiAlpha(reader_
.at(index
+ 1));
641 * Returns true iff the current character is one of `chars`.
643 bool hasOneOf(std::initializer_list
<char16_t
> chars
) const {
644 if (!reader_
.hasMore(1)) {
647 auto ch
= reader_
.current();
648 return std::find(chars
.begin(), chars
.end(), ch
) != chars
.end();
652 * Consumes the current character if it's in `chars` and then returns `true`.
653 * Otherwise returns `false`.
655 bool oneOf(std::initializer_list
<char16_t
> chars
) {
656 if (!hasOneOf(chars
)) {
664 * Consumes the current character if it matches the predicate and then returns
665 * `true`. Otherwise returns `false`.
667 template <typename Predicate
>
668 bool matches(Predicate
&& predicate
) {
669 if (!reader_
.hasMore(1)) {
673 CharT ch
= reader_
.current();
674 if (!predicate(ch
)) {
686 // ASCIISign : one of
688 bool hasSign() const { return hasOneOf({'+', '-', 0x2212}); }
691 * Consumes the current character, which must be a sign character, and returns
695 MOZ_ASSERT(hasSign());
696 int32_t plus
= hasCharacter('+');
698 return plus
? 1 : -1;
701 // DecimalSeparator : one of
703 bool hasDecimalSeparator() const { return hasOneOf({'.', ','}); }
705 bool decimalSeparator() { return oneOf({'.', ','}); }
707 // DaysDesignator : one of
709 bool daysDesignator() { return oneOf({'D', 'd'}); }
711 // HoursDesignator : one of
713 bool hoursDesignator() { return oneOf({'H', 'h'}); }
715 // MinutesDesignator : one of
717 bool minutesDesignator() { return oneOf({'M', 'm'}); }
719 // MonthsDesignator : one of
721 bool monthsDesignator() { return oneOf({'M', 'm'}); }
723 // DurationDesignator : one of
725 bool durationDesignator() { return oneOf({'P', 'p'}); }
727 // SecondsDesignator : one of
729 bool secondsDesignator() { return oneOf({'S', 's'}); }
731 // DateTimeSeparator :
735 bool dateTimeSeparator() { return oneOf({' ', 'T', 't'}); }
737 // TimeDesignator : one of
739 bool hasTimeDesignator() const { return hasOneOf({'T', 't'}); }
741 bool timeDesignator() { return oneOf({'T', 't'}); }
743 // WeeksDesignator : one of
745 bool weeksDesignator() { return oneOf({'W', 'w'}); }
747 // YearsDesignator : one of
749 bool yearsDesignator() { return oneOf({'Y', 'y'}); }
751 // UTCDesignator : one of
753 bool utcDesignator() { return oneOf({'Z', 'z'}); }
759 bool tzLeadingChar() {
760 return matches([](auto ch
) {
761 return mozilla::IsAsciiAlpha(ch
) || ch
== '.' || ch
== '_';
771 return matches([](auto ch
) {
772 return mozilla::IsAsciiAlphanumeric(ch
) || ch
== '.' || ch
== '_' ||
773 ch
== '-' || ch
== '+';
777 // AnnotationCriticalFlag :
779 bool annotationCriticalFlag() { return character('!'); }
784 bool aKeyLeadingChar() {
785 return matches([](auto ch
) {
786 return mozilla::IsAsciiLowercaseAlpha(ch
) || ch
== '_';
795 return matches([](auto ch
) {
796 return mozilla::IsAsciiLowercaseAlpha(ch
) || mozilla::IsAsciiDigit(ch
) ||
797 ch
== '-' || ch
== '_';
801 // AnnotationValueComponent :
802 // Alpha AnnotationValueComponent?
803 // DecimalDigit AnnotationValueComponent?
804 bool annotationValueComponent() {
805 size_t index
= reader_
.index();
807 for (; index
+ i
< reader_
.length(); i
++) {
808 auto ch
= reader_
.at(index
+ i
);
809 if (!mozilla::IsAsciiAlphanumeric(ch
)) {
820 template <typename T
>
821 static constexpr bool inBounds(const T
& x
, const T
& min
, const T
& max
) {
822 return min
<= x
&& x
<= max
;
825 mozilla::Result
<ZonedDateTimeString
, ParserError
> dateTime();
827 mozilla::Result
<PlainDate
, ParserError
> date();
829 mozilla::Result
<PlainDate
, ParserError
> dateSpecYearMonth();
831 mozilla::Result
<PlainDate
, ParserError
> dateSpecMonthDay();
833 mozilla::Result
<PlainDate
, ParserError
> validMonthDay();
835 mozilla::Result
<PlainTime
, ParserError
> timeSpec();
837 // Return true when |Annotation| can start at the current position.
838 bool hasAnnotationStart() const { return hasCharacter('['); }
840 // Return true when |TimeZoneAnnotation| can start at the current position.
841 bool hasTimeZoneAnnotationStart() const {
842 if (!hasCharacter('[')) {
846 // Ensure no '=' is found before the closing ']', otherwise the opening '['
847 // may actually start an |Annotation| instead of a |TimeZoneAnnotation|.
848 for (size_t i
= reader_
.index() + 1; i
< reader_
.length(); i
++) {
849 CharT ch
= reader_
.at(i
);
860 // Return true when |DateTimeUTCOffset| can start at the current position.
861 bool hasDateTimeUTCOffsetStart() {
862 return hasOneOf({'Z', 'z', '+', '-', 0x2212});
865 mozilla::Result
<TimeZoneString
, ParserError
> dateTimeUTCOffset();
867 mozilla::Result
<DateTimeUTCOffset
, ParserError
> utcOffsetSubMinutePrecision();
869 mozilla::Result
<TimeZoneUTCOffset
, ParserError
> timeZoneUTCOffsetName();
871 mozilla::Result
<TimeZoneAnnotation
, ParserError
> timeZoneIdentifier();
873 mozilla::Result
<TimeZoneAnnotation
, ParserError
> timeZoneAnnotation();
875 mozilla::Result
<TimeZoneName
, ParserError
> timeZoneIANAName();
877 mozilla::Result
<AnnotationKey
, ParserError
> annotationKey();
878 mozilla::Result
<AnnotationValue
, ParserError
> annotationValue();
879 mozilla::Result
<Annotation
, ParserError
> annotation();
880 mozilla::Result
<CalendarName
, ParserError
> annotations();
882 mozilla::Result
<ZonedDateTimeString
, ParserError
> annotatedTime();
884 mozilla::Result
<ZonedDateTimeString
, ParserError
> annotatedDateTime();
886 mozilla::Result
<ZonedDateTimeString
, ParserError
>
887 annotatedDateTimeTimeRequired();
889 mozilla::Result
<ZonedDateTimeString
, ParserError
> annotatedYearMonth();
891 mozilla::Result
<ZonedDateTimeString
, ParserError
> annotatedMonthDay();
894 explicit TemporalParser(mozilla::Span
<const CharT
> str
) : reader_(str
) {}
896 mozilla::Result
<ZonedDateTimeString
, ParserError
>
897 parseTemporalInstantString();
899 mozilla::Result
<ZonedDateTimeString
, ParserError
>
900 parseTemporalTimeZoneString();
902 mozilla::Result
<TimeZoneAnnotation
, ParserError
> parseTimeZoneIdentifier();
904 mozilla::Result
<TimeZoneUTCOffset
, ParserError
> parseTimeZoneOffsetString();
906 mozilla::Result
<DateTimeUTCOffset
, ParserError
> parseDateTimeUTCOffset();
908 mozilla::Result
<TemporalDurationString
, ParserError
>
909 parseTemporalDurationString(JSContext
* cx
);
911 mozilla::Result
<ZonedDateTimeString
, ParserError
>
912 parseTemporalCalendarString();
914 mozilla::Result
<ZonedDateTimeString
, ParserError
> parseTemporalTimeString();
916 mozilla::Result
<ZonedDateTimeString
, ParserError
>
917 parseTemporalMonthDayString();
919 mozilla::Result
<ZonedDateTimeString
, ParserError
>
920 parseTemporalYearMonthString();
922 mozilla::Result
<ZonedDateTimeString
, ParserError
>
923 parseTemporalDateTimeString();
925 mozilla::Result
<ZonedDateTimeString
, ParserError
>
926 parseTemporalZonedDateTimeString();
929 template <typename CharT
>
930 mozilla::Result
<ZonedDateTimeString
, ParserError
>
931 TemporalParser
<CharT
>::dateTime() {
934 // Date DateTimeSeparator TimeSpec DateTimeUTCOffset?
935 ZonedDateTimeString result
= {};
939 return dt
.propagateErr();
941 result
.date
= dt
.unwrap();
943 if (dateTimeSeparator()) {
944 auto time
= timeSpec();
946 return time
.propagateErr();
948 result
.time
= time
.unwrap();
950 if (hasDateTimeUTCOffsetStart()) {
951 auto tz
= dateTimeUTCOffset();
953 return tz
.propagateErr();
955 result
.timeZone
= tz
.unwrap();
962 template <typename CharT
>
963 mozilla::Result
<PlainDate
, ParserError
> TemporalParser
<CharT
>::date() {
965 // DateYear - DateMonth - DateDay
966 // DateYear DateMonth DateDay
967 PlainDate result
= {};
971 // Sign DecimalDigit{6}
972 if (auto year
= digits(4)) {
973 result
.year
= year
.value();
974 } else if (hasSign()) {
975 int32_t yearSign
= sign();
976 if (auto year
= digits(6)) {
977 result
.year
= yearSign
* year
.value();
978 if (yearSign
< 0 && result
.year
== 0) {
979 return mozilla::Err(JSMSG_TEMPORAL_PARSER_NEGATIVE_ZERO_YEAR
);
982 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_EXTENDED_YEAR
);
985 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_YEAR
);
996 if (auto month
= digits(2)) {
997 result
.month
= month
.value();
998 if (!inBounds(result
.month
, 1, 12)) {
999 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MONTH
);
1002 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MONTH
);
1014 if (auto day
= digits(2)) {
1015 result
.day
= day
.value();
1016 if (!inBounds(result
.day
, 1, 31)) {
1017 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DAY
);
1020 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DAY
);
1026 template <typename CharT
>
1027 mozilla::Result
<PlainTime
, ParserError
> TemporalParser
<CharT
>::timeSpec() {
1030 // TimeHour : TimeMinute
1031 // TimeHour TimeMinute
1032 // TimeHour : TimeMinute : TimeSecond TimeFraction?
1033 // TimeHour TimeMinute TimeSecond TimeFraction?
1034 PlainTime result
= {};
1040 // [~Padded] DecimalDigit
1041 // [~Padded] 0 DecimalDigit
1047 if (auto hour
= digits(2)) {
1048 result
.hour
= hour
.value();
1049 if (!inBounds(result
.hour
, 0, 23)) {
1050 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_HOUR
);
1053 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_HOUR
);
1057 bool needsMinutes
= character(':');
1069 if (auto minute
= digits(2)) {
1070 result
.minute
= minute
.value();
1071 if (!inBounds(result
.minute
, 0, 59)) {
1072 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MINUTE
);
1076 bool needsSeconds
= needsMinutes
&& character(':');
1081 if (auto second
= digits(2)) {
1082 result
.second
= second
.value();
1083 if (!inBounds(result
.second
, 0, 60)) {
1084 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_LEAPSECOND
);
1089 if (auto f
= fraction()) {
1090 int32_t fractionalPart
= f
.value();
1091 result
.millisecond
= fractionalPart
/ 1'000'000;
1092 result
.microsecond
= (fractionalPart
% 1'000'000) / 1'000;
1093 result
.nanosecond
= fractionalPart
% 1'000;
1095 } else if (needsSeconds
) {
1096 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_SECOND
);
1098 } else if (needsMinutes
) {
1099 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MINUTE
);
1105 template <typename CharT
>
1106 mozilla::Result
<TimeZoneString
, ParserError
>
1107 TemporalParser
<CharT
>::dateTimeUTCOffset() {
1108 // DateTimeUTCOffset :
1110 // UTCOffsetSubMinutePrecision
1112 if (utcDesignator()) {
1113 return TimeZoneString::UTC();
1117 auto offset
= utcOffsetSubMinutePrecision();
1118 if (offset
.isErr()) {
1119 return offset
.propagateErr();
1121 return TimeZoneString::from(offset
.unwrap());
1124 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE
);
1127 template <typename CharT
>
1128 mozilla::Result
<TimeZoneUTCOffset
, ParserError
>
1129 TemporalParser
<CharT
>::timeZoneUTCOffsetName() {
1130 // TimeZoneUTCOffsetName :
1131 // UTCOffsetMinutePrecision
1133 // UTCOffsetMinutePrecision :
1134 // Sign Hour[+Padded]
1135 // Sign Hour[+Padded] TimeSeparator[+Extended] MinuteSecond
1136 // Sign Hour[+Padded] TimeSeparator[~Extended] MinuteSecond
1138 TimeZoneUTCOffset result
= {};
1141 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_SIGN
);
1143 result
.sign
= sign();
1146 // [~Padded] DecimalDigit
1147 // [+Padded] 0 DecimalDigit
1153 if (auto hour
= digits(2)) {
1154 result
.hour
= hour
.value();
1155 if (!inBounds(result
.hour
, 0, 23)) {
1156 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_HOUR
);
1159 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_HOUR
);
1162 // TimeSeparator[Extended] :
1164 // [~Extended] [empty]
1165 bool needsMinutes
= character(':');
1174 if (auto minute
= digits(2)) {
1175 result
.minute
= minute
.value();
1176 if (!inBounds(result
.minute
, 0, 59)) {
1177 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MINUTE
);
1180 if (hasCharacter(':')) {
1181 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_SUBMINUTE_TIMEZONE
);
1183 } else if (needsMinutes
) {
1184 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MINUTE
);
1190 template <typename CharT
>
1191 mozilla::Result
<DateTimeUTCOffset
, ParserError
>
1192 TemporalParser
<CharT
>::utcOffsetSubMinutePrecision() {
1195 // UTCOffsetSubMinutePrecision :
1196 // UTCOffsetMinutePrecision
1197 // UTCOffsetWithSubMinuteComponents[+Extended]
1198 // UTCOffsetWithSubMinuteComponents[~Extended]
1200 // UTCOffsetMinutePrecision :
1201 // Sign Hour[+Padded]
1202 // Sign Hour[+Padded] TimeSeparator[+Extended] MinuteSecond
1203 // Sign Hour[+Padded] TimeSeparator[~Extended] MinuteSecond
1205 // UTCOffsetWithSubMinuteComponents[Extended] :
1206 // Sign Hour[+Padded] TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] MinuteSecond Fraction?
1210 DateTimeUTCOffset result
= {};
1213 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_SIGN
);
1215 result
.sign
= sign();
1218 // [~Padded] DecimalDigit
1219 // [+Padded] 0 DecimalDigit
1225 if (auto hour
= digits(2)) {
1226 result
.hour
= hour
.value();
1227 if (!inBounds(result
.hour
, 0, 23)) {
1228 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_HOUR
);
1231 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_HOUR
);
1234 // TimeSeparator[Extended] :
1236 // [~Extended] [empty]
1237 bool needsMinutes
= character(':');
1246 if (auto minute
= digits(2)) {
1247 result
.minute
= minute
.value();
1248 if (!inBounds(result
.minute
, 0, 59)) {
1249 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MINUTE
);
1252 // TimeSeparator[Extended] :
1254 // [~Extended] [empty]
1255 bool needsSeconds
= needsMinutes
&& character(':');
1264 if (auto second
= digits(2)) {
1265 result
.second
= second
.value();
1266 if (!inBounds(result
.second
, 0, 59)) {
1267 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_SECOND
);
1270 if (auto fractionalPart
= fraction()) {
1271 result
.fractionalPart
= fractionalPart
.value();
1274 result
.subMinutePrecision
= true;
1275 } else if (needsSeconds
) {
1276 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_SECOND
);
1278 } else if (needsMinutes
) {
1279 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MINUTE
);
1285 template <typename CharT
>
1286 mozilla::Result
<TimeZoneAnnotation
, ParserError
>
1287 TemporalParser
<CharT
>::timeZoneIdentifier() {
1288 // TimeZoneIdentifier :
1289 // TimeZoneUTCOffsetName
1292 TimeZoneAnnotation result
= {};
1294 auto offset
= timeZoneUTCOffsetName();
1295 if (offset
.isErr()) {
1296 return offset
.propagateErr();
1298 result
.offset
= offset
.unwrap();
1300 auto name
= timeZoneIANAName();
1302 return name
.propagateErr();
1304 result
.name
= name
.unwrap();
1310 template <typename CharT
>
1311 mozilla::Result
<TimeZoneAnnotation
, ParserError
>
1312 TemporalParser
<CharT
>::timeZoneAnnotation() {
1313 // TimeZoneAnnotation :
1314 // [ AnnotationCriticalFlag? TimeZoneIdentifier ]
1316 if (!character('[')) {
1317 return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_TIMEZONE
);
1320 // Skip over the optional critical flag.
1321 annotationCriticalFlag();
1323 auto result
= timeZoneIdentifier();
1324 if (result
.isErr()) {
1325 return result
.propagateErr();
1328 if (!character(']')) {
1329 return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_TIMEZONE
);
1335 template <typename CharT
>
1336 mozilla::Result
<TimeZoneName
, ParserError
>
1337 TemporalParser
<CharT
>::timeZoneIANAName() {
1338 // TimeZoneIANAName :
1339 // TimeZoneIANANameComponent
1340 // TimeZoneIANAName / TimeZoneIANANameComponent
1342 // TimeZoneIANANameComponent :
1344 // TimeZoneIANANameComponent TZChar
1346 size_t start
= reader_
.index();
1349 if (!tzLeadingChar()) {
1350 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_NAME
);
1353 // Optionally followed by a sequence of |TZChar|.
1356 } while (character('/'));
1358 return TimeZoneName
{start
, reader_
.index() - start
};
1361 template <typename CharT
>
1362 mozilla::Maybe
<double> TemporalParser
<CharT
>::digits(JSContext
* cx
) {
1363 auto span
= reader_
.string().Subspan(reader_
.index());
1365 // GetPrefixInteger can't fail when integer separator handling is disabled.
1366 const CharT
* endp
= nullptr;
1368 MOZ_ALWAYS_TRUE(GetPrefixInteger(span
.data(), span
.data() + span
.size(), 10,
1369 IntegerSeparatorHandling::None
, &endp
,
1372 size_t len
= endp
- span
.data();
1374 return mozilla::Nothing();
1376 reader_
.advance(len
);
1377 return mozilla::Some(num
);
1380 template <typename CharT
>
1381 mozilla::Result
<ZonedDateTimeString
, ParserError
>
1382 TemporalParser
<CharT
>::parseTemporalInstantString() {
1383 // Initialize all fields to zero.
1384 ZonedDateTimeString result
= {};
1388 // TemporalInstantString :
1389 // Date DateTimeSeparator TimeSpec DateTimeUTCOffset TimeZoneAnnotation? Annotations?
1395 return dt
.propagateErr();
1397 result
.date
= dt
.unwrap();
1399 if (!dateTimeSeparator()) {
1400 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DATE_TIME_SEPARATOR
);
1403 auto time
= timeSpec();
1405 return time
.propagateErr();
1407 result
.time
= time
.unwrap();
1409 auto tz
= dateTimeUTCOffset();
1411 return tz
.propagateErr();
1413 result
.timeZone
= tz
.unwrap();
1415 if (hasTimeZoneAnnotationStart()) {
1416 auto annotation
= timeZoneAnnotation();
1417 if (annotation
.isErr()) {
1418 return annotation
.propagateErr();
1420 result
.timeZone
.annotation
= annotation
.unwrap();
1423 if (hasAnnotationStart()) {
1424 if (auto cal
= annotations(); cal
.isErr()) {
1425 return cal
.propagateErr();
1429 if (!reader_
.atEnd()) {
1430 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
1437 * ParseTemporalInstantString ( isoString )
1439 template <typename CharT
>
1440 static auto ParseTemporalInstantString(mozilla::Span
<const CharT
> str
) {
1441 TemporalParser
<CharT
> parser(str
);
1442 return parser
.parseTemporalInstantString();
1446 * ParseTemporalInstantString ( isoString )
1448 static auto ParseTemporalInstantString(Handle
<JSLinearString
*> str
) {
1449 JS::AutoCheckCannotGC nogc
;
1450 if (str
->hasLatin1Chars()) {
1451 return ParseTemporalInstantString
<Latin1Char
>(str
->latin1Range(nogc
));
1453 return ParseTemporalInstantString
<char16_t
>(str
->twoByteRange(nogc
));
1457 * ParseTemporalInstantString ( isoString )
1459 bool js::temporal::ParseTemporalInstantString(JSContext
* cx
,
1460 Handle
<JSString
*> str
,
1461 PlainDateTime
* result
,
1463 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1469 auto parseResult
= ::ParseTemporalInstantString(linear
);
1470 if (parseResult
.isErr()) {
1471 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1472 parseResult
.unwrapErr());
1475 ZonedDateTimeString parsed
= parseResult
.unwrap();
1478 if (!ParseISODateTime(cx
, parsed
, result
)) {
1483 if (parsed
.timeZone
.hasOffset()) {
1484 *offset
= ParseDateTimeUTCOffset(parsed
.timeZone
.offset
);
1486 MOZ_ASSERT(parsed
.timeZone
.isUTC());
1492 template <typename CharT
>
1493 mozilla::Result
<ZonedDateTimeString
, ParserError
>
1494 TemporalParser
<CharT
>::parseTemporalTimeZoneString() {
1495 // TimeZoneIdentifier :
1496 // TimeZoneUTCOffsetName
1500 if (auto offset
= timeZoneUTCOffsetName();
1501 offset
.isOk() && reader_
.atEnd()) {
1502 ZonedDateTimeString result
= {};
1503 result
.timeZone
= TimeZoneString::from(offset
.unwrap());
1507 if (auto name
= timeZoneIANAName(); name
.isOk() && reader_
.atEnd()) {
1508 ZonedDateTimeString result
= {};
1509 result
.timeZone
= TimeZoneString::from(name
.unwrap());
1514 // Try all five parse goals from ParseISODateTime in order.
1516 // TemporalDateTimeString
1517 // TemporalInstantString
1518 // TemporalTimeString
1519 // TemporalMonthDayString
1520 // TemporalYearMonthString
1522 // Restart parsing from the start of the string.
1525 if (auto dt
= parseTemporalDateTimeString(); dt
.isOk()) {
1529 // Restart parsing from the start of the string.
1532 if (auto dt
= parseTemporalInstantString(); dt
.isOk()) {
1536 // Restart parsing from the start of the string.
1539 if (auto dt
= parseTemporalTimeString(); dt
.isOk()) {
1543 // Restart parsing from the start of the string.
1546 if (auto dt
= parseTemporalMonthDayString(); dt
.isOk()) {
1550 // Restart parsing from the start of the string.
1553 if (auto dt
= parseTemporalYearMonthString(); dt
.isOk()) {
1556 return dt
.propagateErr();
1561 * ParseTemporalTimeZoneString ( timeZoneString )
1563 template <typename CharT
>
1564 static auto ParseTemporalTimeZoneString(mozilla::Span
<const CharT
> str
) {
1565 TemporalParser
<CharT
> parser(str
);
1566 return parser
.parseTemporalTimeZoneString();
1570 * ParseTemporalTimeZoneString ( timeZoneString )
1572 static auto ParseTemporalTimeZoneString(Handle
<JSLinearString
*> str
) {
1573 JS::AutoCheckCannotGC nogc
;
1574 if (str
->hasLatin1Chars()) {
1575 return ParseTemporalTimeZoneString
<Latin1Char
>(str
->latin1Range(nogc
));
1577 return ParseTemporalTimeZoneString
<char16_t
>(str
->twoByteRange(nogc
));
1581 * ParseTemporalTimeZoneString ( timeZoneString )
1583 bool js::temporal::ParseTemporalTimeZoneString(
1584 JSContext
* cx
, Handle
<JSString
*> str
,
1585 MutableHandle
<ParsedTimeZone
> result
) {
1586 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1592 auto parseResult
= ::ParseTemporalTimeZoneString(linear
);
1593 if (parseResult
.isErr()) {
1594 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1595 parseResult
.unwrapErr());
1598 ZonedDateTimeString parsed
= parseResult
.unwrap();
1599 const auto& timeZone
= parsed
.timeZone
;
1602 PlainDateTime unused
;
1603 if (!ParseISODateTime(cx
, parsed
, &unused
)) {
1607 if (timeZone
.hasAnnotation()) {
1608 // Case 1: 19700101T00:00Z[+02:00]
1609 // Case 2: 19700101T00:00+00:00[+02:00]
1610 // Case 3: 19700101T00:00[+02:00]
1611 // Case 4: 19700101T00:00Z[Europe/Berlin]
1612 // Case 5: 19700101T00:00+00:00[Europe/Berlin]
1613 // Case 6: 19700101T00:00[Europe/Berlin]
1615 if (!ParseTimeZoneAnnotation(cx
, timeZone
.annotation
, linear
, result
)) {
1618 } else if (timeZone
.isUTC()) {
1619 result
.set(ParsedTimeZone::fromName(cx
->names().UTC
));
1620 } else if (timeZone
.hasOffset()) {
1621 // ToTemporalTimeZoneSlotValue, step 7.
1623 // Error reporting for sub-minute precision moved here.
1624 if (timeZone
.offset
.subMinutePrecision
) {
1625 JS_ReportErrorNumberASCII(
1626 cx
, GetErrorMessage
, nullptr,
1627 JSMSG_TEMPORAL_PARSER_INVALID_SUBMINUTE_TIMEZONE
);
1631 int32_t offset
= ParseTimeZoneOffset(timeZone
.offset
.toTimeZoneUTCOffset());
1632 result
.set(ParsedTimeZone::fromOffset(offset
));
1635 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1636 JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE
);
1644 template <typename CharT
>
1645 mozilla::Result
<TimeZoneAnnotation
, ParserError
>
1646 TemporalParser
<CharT
>::parseTimeZoneIdentifier() {
1647 auto result
= timeZoneIdentifier();
1648 if (result
.isErr()) {
1649 return result
.propagateErr();
1651 if (!reader_
.atEnd()) {
1652 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
1658 * ParseTimeZoneIdentifier ( identifier )
1660 template <typename CharT
>
1661 static auto ParseTimeZoneIdentifier(mozilla::Span
<const CharT
> str
) {
1662 TemporalParser
<CharT
> parser(str
);
1663 return parser
.parseTimeZoneIdentifier();
1667 * ParseTimeZoneIdentifier ( identifier )
1669 static auto ParseTimeZoneIdentifier(Handle
<JSLinearString
*> str
) {
1670 JS::AutoCheckCannotGC nogc
;
1671 if (str
->hasLatin1Chars()) {
1672 return ParseTimeZoneIdentifier
<Latin1Char
>(str
->latin1Range(nogc
));
1674 return ParseTimeZoneIdentifier
<char16_t
>(str
->twoByteRange(nogc
));
1678 * ParseTimeZoneIdentifier ( identifier )
1680 bool js::temporal::ParseTimeZoneIdentifier(
1681 JSContext
* cx
, Handle
<JSString
*> str
,
1682 MutableHandle
<ParsedTimeZone
> result
) {
1683 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1689 auto parseResult
= ::ParseTimeZoneIdentifier(linear
);
1690 if (parseResult
.isErr()) {
1691 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1692 parseResult
.unwrapErr());
1695 auto timeZone
= parseResult
.unwrap();
1698 return ParseTimeZoneAnnotation(cx
, timeZone
, linear
, result
);
1701 template <typename CharT
>
1702 mozilla::Result
<TimeZoneUTCOffset
, ParserError
>
1703 TemporalParser
<CharT
>::parseTimeZoneOffsetString() {
1704 auto offset
= timeZoneUTCOffsetName();
1705 if (offset
.isErr()) {
1706 return offset
.propagateErr();
1708 if (!reader_
.atEnd()) {
1709 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
1711 return offset
.unwrap();
1715 * ParseTimeZoneOffsetString ( isoString )
1717 template <typename CharT
>
1718 static auto ParseTimeZoneOffsetString(mozilla::Span
<const CharT
> str
) {
1719 TemporalParser
<CharT
> parser(str
);
1720 return parser
.parseTimeZoneOffsetString();
1724 * ParseTimeZoneOffsetString ( isoString )
1726 static auto ParseTimeZoneOffsetString(Handle
<JSLinearString
*> str
) {
1727 JS::AutoCheckCannotGC nogc
;
1728 if (str
->hasLatin1Chars()) {
1729 return ParseTimeZoneOffsetString
<Latin1Char
>(str
->latin1Range(nogc
));
1731 return ParseTimeZoneOffsetString
<char16_t
>(str
->twoByteRange(nogc
));
1735 * ParseTimeZoneOffsetString ( isoString )
1737 bool js::temporal::ParseTimeZoneOffsetString(JSContext
* cx
,
1738 Handle
<JSString
*> str
,
1740 // Step 1. (Not applicable in our implementation.)
1742 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1748 auto parseResult
= ::ParseTimeZoneOffsetString(linear
);
1749 if (parseResult
.isErr()) {
1750 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1751 parseResult
.unwrapErr());
1756 *result
= ParseTimeZoneOffset(parseResult
.unwrap());
1760 template <typename CharT
>
1761 mozilla::Result
<DateTimeUTCOffset
, ParserError
>
1762 TemporalParser
<CharT
>::parseDateTimeUTCOffset() {
1763 auto offset
= utcOffsetSubMinutePrecision();
1764 if (offset
.isErr()) {
1765 return offset
.propagateErr();
1767 if (!reader_
.atEnd()) {
1768 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
1770 return offset
.unwrap();
1774 * ParseDateTimeUTCOffset ( offsetString )
1776 template <typename CharT
>
1777 static auto ParseDateTimeUTCOffset(mozilla::Span
<const CharT
> str
) {
1778 TemporalParser
<CharT
> parser(str
);
1779 return parser
.parseDateTimeUTCOffset();
1783 * ParseDateTimeUTCOffset ( offsetString )
1785 static auto ParseDateTimeUTCOffset(Handle
<JSLinearString
*> str
) {
1786 JS::AutoCheckCannotGC nogc
;
1787 if (str
->hasLatin1Chars()) {
1788 return ParseDateTimeUTCOffset
<Latin1Char
>(str
->latin1Range(nogc
));
1790 return ParseDateTimeUTCOffset
<char16_t
>(str
->twoByteRange(nogc
));
1794 * ParseDateTimeUTCOffset ( offsetString )
1796 bool js::temporal::ParseDateTimeUTCOffset(JSContext
* cx
, Handle
<JSString
*> str
,
1798 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1804 auto parseResult
= ::ParseDateTimeUTCOffset(linear
);
1805 if (parseResult
.isErr()) {
1806 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1807 parseResult
.unwrapErr());
1812 *result
= ParseDateTimeUTCOffset(parseResult
.unwrap());
1816 template <typename CharT
>
1817 mozilla::Result
<TemporalDurationString
, ParserError
>
1818 TemporalParser
<CharT
>::parseTemporalDurationString(JSContext
* cx
) {
1819 // Initialize all fields to zero.
1820 TemporalDurationString result
= {};
1822 // TemporalDurationString :
1826 // Sign? DurationDesignator DurationDate
1827 // Sign? DurationDesignator DurationTime
1830 result
.sign
= sign();
1833 if (!durationDesignator()) {
1834 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DESIGNATOR
);
1838 // DurationYearsPart DurationTime?
1839 // DurationMonthsPart DurationTime?
1840 // DurationWeeksPart DurationTime?
1841 // DurationDaysPart DurationTime?
1845 if (hasTimeDesignator()) {
1848 if (auto d
= digits(cx
); !d
) {
1849 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1854 // DurationYearsPart :
1855 // DurationYears YearsDesignator DurationMonthsPart
1856 // DurationYears YearsDesignator DurationWeeksPart
1857 // DurationYears YearsDesignator DurationDaysPart?
1860 // DecimalDigits[~Sep]
1861 if (yearsDesignator()) {
1863 if (reader_
.atEnd()) {
1866 if (hasTimeDesignator()) {
1869 if (auto d
= digits(cx
); !d
) {
1870 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1876 // DurationMonthsPart :
1877 // DurationMonths MonthsDesignator DurationWeeksPart
1878 // DurationMonths MonthsDesignator DurationDaysPart?
1881 // DecimalDigits[~Sep]
1882 if (monthsDesignator()) {
1883 result
.months
= num
;
1884 if (reader_
.atEnd()) {
1887 if (hasTimeDesignator()) {
1890 if (auto d
= digits(cx
); !d
) {
1891 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1897 // DurationWeeksPart :
1898 // DurationWeeks WeeksDesignator DurationDaysPart?
1901 // DecimalDigits[~Sep]
1902 if (weeksDesignator()) {
1904 if (reader_
.atEnd()) {
1907 if (hasTimeDesignator()) {
1910 if (auto d
= digits(cx
); !d
) {
1911 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1917 // DurationDaysPart :
1918 // DurationDays DaysDesignator
1921 // DecimalDigits[~Sep]
1922 if (daysDesignator()) {
1924 if (reader_
.atEnd()) {
1927 if (hasTimeDesignator()) {
1932 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
1936 // DurationTimeDesignator DurationHoursPart
1937 // DurationTimeDesignator DurationMinutesPart
1938 // DurationTimeDesignator DurationSecondsPart
1939 if (!timeDesignator()) {
1940 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_TIME_DESIGNATOR
);
1944 mozilla::Maybe
<int32_t> frac
;
1945 auto digitsAndFraction
= [&]() {
1946 auto d
= digits(cx
);
1955 if (!digitsAndFraction()) {
1956 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1961 // DurationHoursPart :
1962 // DurationWholeHours DurationHoursFraction HoursDesignator
1963 // DurationWholeHours HoursDesignator DurationMinutesPart
1964 // DurationWholeHours HoursDesignator DurationSecondsPart?
1966 // DurationWholeHours :
1967 // DecimalDigits[~Sep]
1969 // DurationHoursFraction :
1976 bool hasHoursFraction
= false;
1977 if (hoursDesignator()) {
1978 hasHoursFraction
= bool(frac
);
1980 result
.hoursFraction
= frac
.valueOr(0);
1981 if (reader_
.atEnd()) {
1984 if (!digitsAndFraction()) {
1985 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
1991 // DurationMinutesPart :
1992 // DurationWholeMinutes DurationMinutesFraction MinutesDesignator
1993 // DurationWholeMinutes MinutesDesignator DurationSecondsPart?
1995 // DurationWholeMinutes :
1996 // DecimalDigits[~Sep]
1998 // DurationMinutesFraction :
2005 bool hasMinutesFraction
= false;
2006 if (minutesDesignator()) {
2007 if (hasHoursFraction
) {
2008 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_MINUTES
);
2010 hasMinutesFraction
= bool(frac
);
2011 result
.minutes
= num
;
2012 result
.minutesFraction
= frac
.valueOr(0);
2013 if (reader_
.atEnd()) {
2016 if (!digitsAndFraction()) {
2017 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS
);
2021 // DurationSecondsPart :
2022 // DurationWholeSeconds DurationSecondsFraction? SecondsDesignator
2024 // DurationWholeSeconds :
2025 // DecimalDigits[~Sep]
2027 // DurationSecondsFraction :
2032 if (secondsDesignator()) {
2033 if (hasHoursFraction
|| hasMinutesFraction
) {
2034 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_SECONDS
);
2036 result
.seconds
= num
;
2037 result
.secondsFraction
= frac
.valueOr(0);
2038 if (reader_
.atEnd()) {
2043 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
2047 * ParseTemporalDurationString ( isoString )
2049 template <typename CharT
>
2050 static auto ParseTemporalDurationString(JSContext
* cx
,
2051 mozilla::Span
<const CharT
> str
) {
2052 TemporalParser
<CharT
> parser(str
);
2053 return parser
.parseTemporalDurationString(cx
);
2057 * ParseTemporalDurationString ( isoString )
2059 static auto ParseTemporalDurationString(JSContext
* cx
,
2060 Handle
<JSLinearString
*> str
) {
2061 JS::AutoCheckCannotGC nogc
;
2062 if (str
->hasLatin1Chars()) {
2063 return ParseTemporalDurationString
<Latin1Char
>(cx
, str
->latin1Range(nogc
));
2065 return ParseTemporalDurationString
<char16_t
>(cx
, str
->twoByteRange(nogc
));
2069 * ParseTemporalDurationString ( isoString )
2071 bool js::temporal::ParseTemporalDurationString(JSContext
* cx
,
2072 Handle
<JSString
*> str
,
2074 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
2080 auto parseResult
= ::ParseTemporalDurationString(cx
, linear
);
2081 if (parseResult
.isErr()) {
2082 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2083 parseResult
.unwrapErr());
2086 TemporalDurationString parsed
= parseResult
.unwrap();
2089 double years
= parsed
.years
;
2090 double months
= parsed
.months
;
2091 double weeks
= parsed
.weeks
;
2092 double days
= parsed
.days
;
2093 double hours
= parsed
.hours
;
2096 double minutes
, seconds
, milliseconds
, microseconds
, nanoseconds
;
2097 if (parsed
.hoursFraction
) {
2098 MOZ_ASSERT(parsed
.hoursFraction
> 0);
2099 MOZ_ASSERT(parsed
.hoursFraction
< 1'000'000'000);
2102 MOZ_ASSERT(parsed
.minutes
== 0);
2103 MOZ_ASSERT(parsed
.minutesFraction
== 0);
2104 MOZ_ASSERT(parsed
.seconds
== 0);
2105 MOZ_ASSERT(parsed
.secondsFraction
== 0);
2108 int64_t h
= int64_t(parsed
.hoursFraction
) * 60;
2109 minutes
= h
/ 1'000'000'000;
2111 // Steps 13 and 15-17.
2112 int64_t min
= (h
% 1'000'000'000) * 60;
2113 seconds
= min
/ 1'000'000'000;
2114 milliseconds
= (min
% 1'000'000'000) / 1'000'000;
2115 microseconds
= (min
% 1'000'000) / 1'000;
2116 nanoseconds
= (min
% 1'000);
2120 else if (parsed
.minutesFraction
) {
2121 MOZ_ASSERT(parsed
.minutesFraction
> 0);
2122 MOZ_ASSERT(parsed
.minutesFraction
< 1'000'000'000);
2125 MOZ_ASSERT(parsed
.seconds
== 0);
2126 MOZ_ASSERT(parsed
.secondsFraction
== 0);
2129 minutes
= parsed
.minutes
;
2131 // Steps 11.b-d and 15-17.
2132 int64_t min
= int64_t(parsed
.minutesFraction
) * 60;
2133 seconds
= min
/ 1'000'000'000;
2134 milliseconds
= (min
% 1'000'000'000) / 1'000'000;
2135 microseconds
= (min
% 1'000'000) / 1'000;
2136 nanoseconds
= (min
% 1'000);
2140 else if (parsed
.secondsFraction
) {
2141 MOZ_ASSERT(parsed
.secondsFraction
> 0);
2142 MOZ_ASSERT(parsed
.secondsFraction
< 1'000'000'000);
2145 minutes
= parsed
.minutes
;
2148 seconds
= parsed
.seconds
;
2151 milliseconds
= (parsed
.secondsFraction
/ 1'000'000);
2152 microseconds
= ((parsed
.secondsFraction
% 1'000'000) / 1'000);
2153 nanoseconds
= (parsed
.secondsFraction
% 1'000);
2156 minutes
= parsed
.minutes
;
2159 seconds
= parsed
.seconds
;
2168 int32_t factor
= parsed
.sign
? parsed
.sign
: 1;
2169 MOZ_ASSERT(factor
== -1 || factor
== 1);
2173 (years
* factor
) + (+0.0), (months
* factor
) + (+0.0),
2174 (weeks
* factor
) + (+0.0), (days
* factor
) + (+0.0),
2175 (hours
* factor
) + (+0.0), (minutes
* factor
) + (+0.0),
2176 (seconds
* factor
) + (+0.0), (milliseconds
* factor
) + (+0.0),
2177 (microseconds
* factor
) + (+0.0), (nanoseconds
* factor
) + (+0.0),
2181 if (!ThrowIfInvalidDuration(cx
, *result
)) {
2189 template <typename CharT
>
2190 mozilla::Result
<AnnotationKey
, ParserError
>
2191 TemporalParser
<CharT
>::annotationKey() {
2194 // AnnotationKey AKeyChar
2196 size_t start
= reader_
.index();
2198 if (!aKeyLeadingChar()) {
2199 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_KEY
);
2202 // Optionally followed by a sequence of |AKeyChar|.
2203 while (aKeyChar()) {
2206 return AnnotationKey
{start
, reader_
.index() - start
};
2209 template <typename CharT
>
2210 mozilla::Result
<AnnotationValue
, ParserError
>
2211 TemporalParser
<CharT
>::annotationValue() {
2212 // AnnotationValue :
2213 // AnnotationValueComponent
2214 // AnnotationValueComponent - AnnotationValue
2216 size_t start
= reader_
.index();
2219 if (!annotationValueComponent()) {
2220 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_VALUE
);
2222 } while (character('-'));
2224 return AnnotationValue
{start
, reader_
.index() - start
};
2227 template <typename CharT
>
2228 mozilla::Result
<Annotation
, ParserError
> TemporalParser
<CharT
>::annotation() {
2230 // [ AnnotationCriticalFlag? AnnotationKey = AnnotationValue ]
2232 if (!character('[')) {
2233 return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_ANNOTATION
);
2236 bool critical
= annotationCriticalFlag();
2238 auto key
= annotationKey();
2240 return key
.propagateErr();
2243 if (!character('=')) {
2244 return mozilla::Err(JSMSG_TEMPORAL_PARSER_ASSIGNMENT_IN_ANNOTATION
);
2247 auto value
= annotationValue();
2248 if (value
.isErr()) {
2249 return value
.propagateErr();
2252 if (!character(']')) {
2253 return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_ANNOTATION
);
2256 return Annotation
{key
.unwrap(), value
.unwrap(), critical
};
2259 template <typename CharT
>
2260 mozilla::Result
<CalendarName
, ParserError
>
2261 TemporalParser
<CharT
>::annotations() {
2263 // Annotation Annotations?
2265 MOZ_ASSERT(hasAnnotationStart());
2267 CalendarName calendar
;
2268 bool calendarWasCritical
= false;
2269 while (hasAnnotationStart()) {
2270 auto anno
= annotation();
2272 return anno
.propagateErr();
2274 auto [key
, value
, critical
] = anno
.unwrap();
2276 // FIXME: spec issue - ignore case for "[u-ca=" to match BCP47?
2277 // https://github.com/tc39/proposal-temporal/issues/2524
2279 static constexpr std::string_view ca
= "u-ca";
2281 auto keySpan
= reader_
.substring(key
);
2282 if (keySpan
.size() == ca
.length() &&
2283 std::equal(ca
.begin(), ca
.end(), keySpan
.data())) {
2284 if (!calendar
.present()) {
2286 calendarWasCritical
= critical
;
2287 } else if (critical
|| calendarWasCritical
) {
2288 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_CRITICAL_ANNOTATION
);
2290 } else if (critical
) {
2291 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_CRITICAL_ANNOTATION
);
2297 template <typename CharT
>
2298 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2299 TemporalParser
<CharT
>::annotatedTime() {
2303 // TimeDesignator TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations?
2304 // TimeSpecWithOptionalOffsetNotAmbiguous TimeZoneAnnotation? Annotations?
2308 if (timeDesignator()) {
2309 ZonedDateTimeString result
= {};
2311 auto time
= timeSpec();
2313 return time
.propagateErr();
2315 result
.time
= time
.unwrap();
2317 if (hasDateTimeUTCOffsetStart()) {
2318 auto tz
= dateTimeUTCOffset();
2320 return tz
.propagateErr();
2322 result
.timeZone
= tz
.unwrap();
2325 if (hasTimeZoneAnnotationStart()) {
2326 auto annotation
= timeZoneAnnotation();
2327 if (annotation
.isErr()) {
2328 return annotation
.propagateErr();
2330 result
.timeZone
.annotation
= annotation
.unwrap();
2333 if (hasAnnotationStart()) {
2334 auto cal
= annotations();
2336 return cal
.propagateErr();
2338 result
.calendar
= cal
.unwrap();
2346 // TimeSpecWithOptionalOffsetNotAmbiguous :
2347 // TimeSpec DateTimeUTCOffset? but not one of ValidMonthDay or DateSpecYearMonth
2351 size_t start
= reader_
.index();
2353 ZonedDateTimeString result
= {};
2355 auto time
= timeSpec();
2357 return time
.propagateErr();
2359 result
.time
= time
.unwrap();
2361 if (hasDateTimeUTCOffsetStart()) {
2362 auto tz
= dateTimeUTCOffset();
2364 return tz
.propagateErr();
2366 result
.timeZone
= tz
.unwrap();
2369 size_t end
= reader_
.index();
2371 // Reset and check if the input can also be parsed as ValidMonthDay.
2372 reader_
.reset(start
);
2374 if (validMonthDay().isOk()) {
2375 if (reader_
.index() == end
) {
2376 return mozilla::Err(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_MONTH_DAY
);
2380 // Reset and check if the input can also be parsed as DateSpecYearMonth.
2381 reader_
.reset(start
);
2383 if (dateSpecYearMonth().isOk()) {
2384 if (reader_
.index() == end
) {
2385 return mozilla::Err(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_YEAR_MONTH
);
2389 // Input can neither be parsed as ValidMonthDay nor DateSpecYearMonth.
2392 if (hasTimeZoneAnnotationStart()) {
2393 auto annotation
= timeZoneAnnotation();
2394 if (annotation
.isErr()) {
2395 return annotation
.propagateErr();
2397 result
.timeZone
.annotation
= annotation
.unwrap();
2400 if (hasAnnotationStart()) {
2401 auto cal
= annotations();
2403 return cal
.propagateErr();
2405 result
.calendar
= cal
.unwrap();
2411 template <typename CharT
>
2412 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2413 TemporalParser
<CharT
>::annotatedDateTime() {
2414 // AnnotatedDateTime[Zoned] :
2415 // [~Zoned] DateTime TimeZoneAnnotation? Annotations?
2416 // [+Zoned] DateTime TimeZoneAnnotation Annotations?
2418 auto dt
= dateTime();
2420 return dt
.propagateErr();
2422 auto result
= dt
.unwrap();
2424 if (hasTimeZoneAnnotationStart()) {
2425 auto annotation
= timeZoneAnnotation();
2426 if (annotation
.isErr()) {
2427 return annotation
.propagateErr();
2429 result
.timeZone
.annotation
= annotation
.unwrap();
2432 if (hasAnnotationStart()) {
2433 auto cal
= annotations();
2435 return cal
.propagateErr();
2437 result
.calendar
= cal
.unwrap();
2443 template <typename CharT
>
2444 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2445 TemporalParser
<CharT
>::annotatedDateTimeTimeRequired() {
2448 // AnnotatedDateTimeTimeRequired :
2449 // Date DateTimeSeparator TimeSpec DateTimeUTCOffset? TimeZoneAnnotation? Annotations?
2453 ZonedDateTimeString result
= {};
2457 return dt
.propagateErr();
2459 result
.date
= dt
.unwrap();
2461 if (!dateTimeSeparator()) {
2462 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DATE_TIME_SEPARATOR
);
2465 auto time
= timeSpec();
2467 return time
.propagateErr();
2469 result
.time
= time
.unwrap();
2471 if (hasDateTimeUTCOffsetStart()) {
2472 auto tz
= dateTimeUTCOffset();
2474 return tz
.propagateErr();
2476 result
.timeZone
= tz
.unwrap();
2479 if (hasTimeZoneAnnotationStart()) {
2480 auto annotation
= timeZoneAnnotation();
2481 if (annotation
.isErr()) {
2482 return annotation
.propagateErr();
2484 result
.timeZone
.annotation
= annotation
.unwrap();
2487 if (hasAnnotationStart()) {
2488 auto cal
= annotations();
2490 return cal
.propagateErr();
2492 result
.calendar
= cal
.unwrap();
2498 template <typename CharT
>
2499 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2500 TemporalParser
<CharT
>::annotatedYearMonth() {
2501 // AnnotatedYearMonth :
2502 // DateSpecYearMonth TimeZoneAnnotation? Annotations?
2504 ZonedDateTimeString result
= {};
2506 auto yearMonth
= dateSpecYearMonth();
2507 if (yearMonth
.isErr()) {
2508 return yearMonth
.propagateErr();
2510 result
.date
= yearMonth
.unwrap();
2512 if (hasTimeZoneAnnotationStart()) {
2513 auto annotation
= timeZoneAnnotation();
2514 if (annotation
.isErr()) {
2515 return annotation
.propagateErr();
2517 result
.timeZone
.annotation
= annotation
.unwrap();
2520 if (hasAnnotationStart()) {
2521 auto cal
= annotations();
2523 return cal
.propagateErr();
2525 result
.calendar
= cal
.unwrap();
2531 template <typename CharT
>
2532 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2533 TemporalParser
<CharT
>::annotatedMonthDay() {
2534 // AnnotatedMonthDay :
2535 // DateSpecMonthDay TimeZoneAnnotation? Annotations?
2537 ZonedDateTimeString result
= {};
2539 auto monthDay
= dateSpecMonthDay();
2540 if (monthDay
.isErr()) {
2541 return monthDay
.propagateErr();
2543 result
.date
= monthDay
.unwrap();
2545 if (hasTimeZoneAnnotationStart()) {
2546 auto annotation
= timeZoneAnnotation();
2547 if (annotation
.isErr()) {
2548 return annotation
.propagateErr();
2550 result
.timeZone
.annotation
= annotation
.unwrap();
2553 if (hasAnnotationStart()) {
2554 auto cal
= annotations();
2556 return cal
.propagateErr();
2558 result
.calendar
= cal
.unwrap();
2564 template <typename CharT
>
2565 mozilla::Result
<PlainDate
, ParserError
>
2566 TemporalParser
<CharT
>::dateSpecYearMonth() {
2567 // DateSpecYearMonth :
2568 // DateYear -? DateMonth
2569 PlainDate result
= {};
2573 // Sign DecimalDigit{6}
2574 if (auto year
= digits(4)) {
2575 result
.year
= year
.value();
2576 } else if (hasSign()) {
2577 int32_t yearSign
= sign();
2578 if (auto year
= digits(6)) {
2579 result
.year
= yearSign
* year
.value();
2580 if (yearSign
< 0 && result
.year
== 0) {
2581 return mozilla::Err(JSMSG_TEMPORAL_PARSER_NEGATIVE_ZERO_YEAR
);
2584 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_EXTENDED_YEAR
);
2587 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_YEAR
);
2597 if (auto month
= digits(2)) {
2598 result
.month
= month
.value();
2599 if (!inBounds(result
.month
, 1, 12)) {
2600 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MONTH
);
2603 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MONTH
);
2606 // Absent days default to 1, cf. ParseISODateTime.
2612 template <typename CharT
>
2613 mozilla::Result
<PlainDate
, ParserError
>
2614 TemporalParser
<CharT
>::dateSpecMonthDay() {
2615 // DateSpecMonthDay :
2616 // -- DateMonth -? DateDay
2617 // DateMonth -? DateDay
2618 PlainDate result
= {};
2622 result
.year
= AbsentYear
;
2629 if (auto month
= digits(2)) {
2630 result
.month
= month
.value();
2631 if (!inBounds(result
.month
, 1, 12)) {
2632 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MONTH
);
2635 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MONTH
);
2646 if (auto day
= digits(2)) {
2647 result
.day
= day
.value();
2648 if (!inBounds(result
.day
, 1, 31)) {
2649 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DAY
);
2652 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DAY
);
2658 template <typename CharT
>
2659 mozilla::Result
<PlainDate
, ParserError
> TemporalParser
<CharT
>::validMonthDay() {
2661 // DateMonth -? 0 NonZeroDigit
2662 // DateMonth -? 1 DecimalDigit
2663 // DateMonth -? 2 DecimalDigit
2664 // DateMonth -? 30 but not one of 0230 or 02-30
2665 // DateMonthWithThirtyOneDays -? 31
2667 // DateMonthWithThirtyOneDays : one of
2668 // 01 03 05 07 08 10 12
2670 PlainDate result
= {};
2677 if (auto month
= digits(2)) {
2678 result
.month
= month
.value();
2679 if (!inBounds(result
.month
, 1, 12)) {
2680 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_MONTH
);
2683 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_MONTH
);
2688 if (auto day
= digits(2)) {
2689 result
.day
= day
.value();
2690 if (!inBounds(result
.day
, 1, 31)) {
2691 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DAY
);
2694 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MISSING_DAY
);
2697 if (result
.month
== 2 && result
.day
> 29) {
2698 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DAY
);
2701 if (result
.day
> 30) {
2702 MOZ_ASSERT(result
.day
== 31);
2704 static constexpr int32_t monthsWithThirtyOneDays
[] = {
2705 1, 3, 5, 7, 8, 10, 12,
2708 if (std::find(std::begin(monthsWithThirtyOneDays
),
2709 std::end(monthsWithThirtyOneDays
),
2710 result
.month
) == std::end(monthsWithThirtyOneDays
)) {
2711 return mozilla::Err(JSMSG_TEMPORAL_PARSER_INVALID_DAY
);
2718 template <typename CharT
>
2719 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2720 TemporalParser
<CharT
>::parseTemporalCalendarString() {
2721 // Handle the common case of a standalone calendar name first.
2723 // All valid calendar names start with two alphabetic characters and none of
2724 // the ParseISODateTime parse goals can start with two alphabetic characters.
2725 // TemporalTimeString can start with 'T', so we can't only check the first
2727 if (hasTwoAsciiAlpha()) {
2728 auto cal
= annotationValue();
2730 return cal
.propagateErr();
2732 if (!reader_
.atEnd()) {
2733 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
2736 ZonedDateTimeString result
= {};
2737 result
.calendar
= cal
.unwrap();
2741 // Try all five parse goals from ParseISODateTime in order.
2743 // TemporalDateTimeString
2744 // TemporalInstantString
2745 // TemporalTimeString
2746 // TemporalZonedDateTimeString
2747 // TemporalMonthDayString
2748 // TemporalYearMonthString
2750 if (auto dt
= parseTemporalDateTimeString(); dt
.isOk()) {
2754 // Restart parsing from the start of the string.
2757 if (auto dt
= parseTemporalInstantString(); dt
.isOk()) {
2761 // Restart parsing from the start of the string.
2764 if (auto dt
= parseTemporalTimeString(); dt
.isOk()) {
2768 // Restart parsing from the start of the string.
2771 if (auto dt
= parseTemporalMonthDayString(); dt
.isOk()) {
2775 // Restart parsing from the start of the string.
2778 if (auto dt
= parseTemporalYearMonthString(); dt
.isOk()) {
2781 return dt
.propagateErr();
2786 * ParseTemporalCalendarString ( isoString )
2788 template <typename CharT
>
2789 static auto ParseTemporalCalendarString(mozilla::Span
<const CharT
> str
) {
2790 TemporalParser
<CharT
> parser(str
);
2791 return parser
.parseTemporalCalendarString();
2795 * ParseTemporalCalendarString ( isoString )
2797 static auto ParseTemporalCalendarString(Handle
<JSLinearString
*> str
) {
2798 JS::AutoCheckCannotGC nogc
;
2799 if (str
->hasLatin1Chars()) {
2800 return ParseTemporalCalendarString
<Latin1Char
>(str
->latin1Range(nogc
));
2802 return ParseTemporalCalendarString
<char16_t
>(str
->twoByteRange(nogc
));
2806 * ParseTemporalCalendarString ( isoString )
2808 JSLinearString
* js::temporal::ParseTemporalCalendarString(
2809 JSContext
* cx
, Handle
<JSString
*> str
) {
2810 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
2816 auto parseResult
= ::ParseTemporalCalendarString(linear
);
2817 if (parseResult
.isErr()) {
2818 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2819 parseResult
.unwrapErr());
2822 ZonedDateTimeString parsed
= parseResult
.unwrap();
2824 PlainDateTime unused
;
2825 if (!ParseISODateTime(cx
, parsed
, &unused
)) {
2830 if (!parsed
.calendar
.present()) {
2831 return cx
->names().iso8601
;
2834 // Steps 2.c and 3.c
2835 return ToString(cx
, linear
, parsed
.calendar
);
2838 template <typename CharT
>
2839 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2840 TemporalParser
<CharT
>::parseTemporalTimeString() {
2841 // TemporalTimeString :
2843 // AnnotatedDateTimeTimeRequired
2845 if (auto time
= annotatedTime(); time
.isOk() && reader_
.atEnd()) {
2846 return time
.unwrap();
2849 // Reset and try the next option.
2852 auto dt
= annotatedDateTimeTimeRequired();
2854 return dt
.propagateErr();
2856 if (!reader_
.atEnd()) {
2857 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
2863 * ParseTemporalTimeString ( isoString )
2865 template <typename CharT
>
2866 static auto ParseTemporalTimeString(mozilla::Span
<const CharT
> str
) {
2867 TemporalParser
<CharT
> parser(str
);
2868 return parser
.parseTemporalTimeString();
2872 * ParseTemporalTimeString ( isoString )
2874 static auto ParseTemporalTimeString(Handle
<JSLinearString
*> str
) {
2875 JS::AutoCheckCannotGC nogc
;
2876 if (str
->hasLatin1Chars()) {
2877 return ParseTemporalTimeString
<Latin1Char
>(str
->latin1Range(nogc
));
2879 return ParseTemporalTimeString
<char16_t
>(str
->twoByteRange(nogc
));
2883 * ParseTemporalTimeString ( isoString )
2885 bool js::temporal::ParseTemporalTimeString(JSContext
* cx
, Handle
<JSString
*> str
,
2886 PlainTime
* result
) {
2887 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
2893 auto parseResult
= ::ParseTemporalTimeString(linear
);
2894 if (parseResult
.isErr()) {
2895 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2896 parseResult
.unwrapErr());
2899 ZonedDateTimeString parsed
= parseResult
.unwrap();
2902 if (parsed
.timeZone
.isUTC()) {
2903 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2904 JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR
);
2909 PlainDateTime dateTime
;
2910 if (!ParseISODateTime(cx
, parsed
, &dateTime
)) {
2913 *result
= dateTime
.time
;
2919 template <typename CharT
>
2920 mozilla::Result
<ZonedDateTimeString
, ParserError
>
2921 TemporalParser
<CharT
>::parseTemporalMonthDayString() {
2922 // TemporalMonthDayString :
2923 // AnnotatedMonthDay
2924 // AnnotatedDateTime[~Zoned]
2926 if (auto monthDay
= annotatedMonthDay(); monthDay
.isOk() && reader_
.atEnd()) {
2927 auto result
= monthDay
.unwrap();
2929 // ParseISODateTime, step 3.
2930 if (result
.calendar
.present() &&
2931 !IsISO8601Calendar(reader_
.substring(result
.calendar
))) {
2932 return mozilla::Err(JSMSG_TEMPORAL_PARSER_MONTH_DAY_CALENDAR_NOT_ISO8601
);
2937 // Reset and try the next option.
2940 auto dt
= annotatedDateTime();
2942 return dt
.propagateErr();
2944 if (!reader_
.atEnd()) {
2945 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
2951 * ParseTemporalMonthDayString ( isoString )
2953 template <typename CharT
>
2954 static auto ParseTemporalMonthDayString(mozilla::Span
<const CharT
> str
) {
2955 TemporalParser
<CharT
> parser(str
);
2956 return parser
.parseTemporalMonthDayString();
2960 * ParseTemporalMonthDayString ( isoString )
2962 static auto ParseTemporalMonthDayString(Handle
<JSLinearString
*> str
) {
2963 JS::AutoCheckCannotGC nogc
;
2964 if (str
->hasLatin1Chars()) {
2965 return ParseTemporalMonthDayString
<Latin1Char
>(str
->latin1Range(nogc
));
2967 return ParseTemporalMonthDayString
<char16_t
>(str
->twoByteRange(nogc
));
2971 * ParseTemporalMonthDayString ( isoString )
2973 bool js::temporal::ParseTemporalMonthDayString(
2974 JSContext
* cx
, Handle
<JSString
*> str
, PlainDate
* result
, bool* hasYear
,
2975 MutableHandle
<JSString
*> calendar
) {
2976 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
2982 auto parseResult
= ::ParseTemporalMonthDayString(linear
);
2983 if (parseResult
.isErr()) {
2984 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2985 parseResult
.unwrapErr());
2988 ZonedDateTimeString parsed
= parseResult
.unwrap();
2991 if (parsed
.timeZone
.isUTC()) {
2992 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2993 JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR
);
2998 PlainDateTime dateTime
;
2999 if (!ParseISODateTime(cx
, parsed
, &dateTime
)) {
3002 *result
= dateTime
.date
;
3005 *hasYear
= parsed
.date
.year
!= AbsentYear
;
3007 if (parsed
.calendar
.present()) {
3008 calendar
.set(ToString(cx
, linear
, parsed
.calendar
));
3018 template <typename CharT
>
3019 mozilla::Result
<ZonedDateTimeString
, ParserError
>
3020 TemporalParser
<CharT
>::parseTemporalYearMonthString() {
3021 // TemporalYearMonthString :
3022 // AnnotatedYearMonth
3023 // AnnotatedDateTime[~Zoned]
3025 if (auto yearMonth
= annotatedYearMonth();
3026 yearMonth
.isOk() && reader_
.atEnd()) {
3027 auto result
= yearMonth
.unwrap();
3029 // ParseISODateTime, step 3.
3030 if (result
.calendar
.present() &&
3031 !IsISO8601Calendar(reader_
.substring(result
.calendar
))) {
3032 return mozilla::Err(
3033 JSMSG_TEMPORAL_PARSER_YEAR_MONTH_CALENDAR_NOT_ISO8601
);
3038 // Reset and try the next option.
3041 auto dt
= annotatedDateTime();
3043 return dt
.propagateErr();
3045 if (!reader_
.atEnd()) {
3046 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
3052 * ParseTemporalYearMonthString ( isoString )
3054 template <typename CharT
>
3055 static auto ParseTemporalYearMonthString(mozilla::Span
<const CharT
> str
) {
3056 TemporalParser
<CharT
> parser(str
);
3057 return parser
.parseTemporalYearMonthString();
3061 * ParseTemporalYearMonthString ( isoString )
3063 static auto ParseTemporalYearMonthString(Handle
<JSLinearString
*> str
) {
3064 JS::AutoCheckCannotGC nogc
;
3065 if (str
->hasLatin1Chars()) {
3066 return ParseTemporalYearMonthString
<Latin1Char
>(str
->latin1Range(nogc
));
3068 return ParseTemporalYearMonthString
<char16_t
>(str
->twoByteRange(nogc
));
3072 * ParseTemporalYearMonthString ( isoString )
3074 bool js::temporal::ParseTemporalYearMonthString(
3075 JSContext
* cx
, Handle
<JSString
*> str
, PlainDate
* result
,
3076 MutableHandle
<JSString
*> calendar
) {
3077 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
3083 auto parseResult
= ::ParseTemporalYearMonthString(linear
);
3084 if (parseResult
.isErr()) {
3085 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3086 parseResult
.unwrapErr());
3089 ZonedDateTimeString parsed
= parseResult
.unwrap();
3092 if (parsed
.timeZone
.isUTC()) {
3093 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3094 JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR
);
3099 PlainDateTime dateTime
;
3100 if (!ParseISODateTime(cx
, parsed
, &dateTime
)) {
3103 *result
= dateTime
.date
;
3105 if (parsed
.calendar
.present()) {
3106 calendar
.set(ToString(cx
, linear
, parsed
.calendar
));
3116 template <typename CharT
>
3117 mozilla::Result
<ZonedDateTimeString
, ParserError
>
3118 TemporalParser
<CharT
>::parseTemporalDateTimeString() {
3119 // TemporalDateTimeString[Zoned] :
3120 // AnnotatedDateTime[?Zoned]
3122 auto dateTime
= annotatedDateTime();
3123 if (dateTime
.isErr()) {
3124 return dateTime
.propagateErr();
3126 if (!reader_
.atEnd()) {
3127 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
3129 return dateTime
.unwrap();
3133 * ParseTemporalDateTimeString ( isoString )
3135 template <typename CharT
>
3136 static auto ParseTemporalDateTimeString(mozilla::Span
<const CharT
> str
) {
3137 TemporalParser
<CharT
> parser(str
);
3138 return parser
.parseTemporalDateTimeString();
3142 * ParseTemporalDateTimeString ( isoString )
3144 static auto ParseTemporalDateTimeString(Handle
<JSLinearString
*> str
) {
3145 JS::AutoCheckCannotGC nogc
;
3146 if (str
->hasLatin1Chars()) {
3147 return ParseTemporalDateTimeString
<Latin1Char
>(str
->latin1Range(nogc
));
3149 return ParseTemporalDateTimeString
<char16_t
>(str
->twoByteRange(nogc
));
3153 * ParseTemporalDateTimeString ( isoString )
3155 bool js::temporal::ParseTemporalDateTimeString(
3156 JSContext
* cx
, Handle
<JSString
*> str
, PlainDateTime
* result
,
3157 MutableHandle
<JSString
*> calendar
) {
3158 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
3164 auto parseResult
= ::ParseTemporalDateTimeString(linear
);
3165 if (parseResult
.isErr()) {
3166 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3167 parseResult
.unwrapErr());
3170 ZonedDateTimeString parsed
= parseResult
.unwrap();
3173 if (parsed
.timeZone
.isUTC()) {
3174 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3175 JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR
);
3180 if (!ParseISODateTime(cx
, parsed
, result
)) {
3184 if (parsed
.calendar
.present()) {
3185 calendar
.set(ToString(cx
, linear
, parsed
.calendar
));
3195 * ParseTemporalDateString ( isoString )
3197 bool js::temporal::ParseTemporalDateString(JSContext
* cx
, Handle
<JSString
*> str
,
3199 MutableHandle
<JSString
*> calendar
) {
3201 PlainDateTime dateTime
;
3202 if (!ParseTemporalDateTimeString(cx
, str
, &dateTime
, calendar
)) {
3207 *result
= dateTime
.date
;
3211 template <typename CharT
>
3212 mozilla::Result
<ZonedDateTimeString
, ParserError
>
3213 TemporalParser
<CharT
>::parseTemporalZonedDateTimeString() {
3214 // Parse goal: TemporalDateTimeString[+Zoned]
3216 // TemporalDateTimeString[Zoned] :
3217 // AnnotatedDateTime[?Zoned]
3219 // AnnotatedDateTime[Zoned] :
3220 // [~Zoned] DateTime TimeZoneAnnotation? Annotations?
3221 // [+Zoned] DateTime TimeZoneAnnotation Annotations?
3223 auto dt
= dateTime();
3225 return dt
.propagateErr();
3227 auto result
= dt
.unwrap();
3229 auto annotation
= timeZoneAnnotation();
3230 if (annotation
.isErr()) {
3231 return annotation
.propagateErr();
3233 result
.timeZone
.annotation
= annotation
.unwrap();
3235 if (hasAnnotationStart()) {
3236 auto cal
= annotations();
3238 return cal
.propagateErr();
3240 result
.calendar
= cal
.unwrap();
3243 if (!reader_
.atEnd()) {
3244 return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT
);
3251 * ParseTemporalZonedDateTimeString ( isoString )
3253 template <typename CharT
>
3254 static auto ParseTemporalZonedDateTimeString(mozilla::Span
<const CharT
> str
) {
3255 TemporalParser
<CharT
> parser(str
);
3256 return parser
.parseTemporalZonedDateTimeString();
3260 * ParseTemporalZonedDateTimeString ( isoString )
3262 static auto ParseTemporalZonedDateTimeString(Handle
<JSLinearString
*> str
) {
3263 JS::AutoCheckCannotGC nogc
;
3264 if (str
->hasLatin1Chars()) {
3265 return ParseTemporalZonedDateTimeString
<Latin1Char
>(str
->latin1Range(nogc
));
3267 return ParseTemporalZonedDateTimeString
<char16_t
>(str
->twoByteRange(nogc
));
3271 * ParseTemporalZonedDateTimeString ( isoString )
3273 bool js::temporal::ParseTemporalZonedDateTimeString(
3274 JSContext
* cx
, Handle
<JSString
*> str
, PlainDateTime
* dateTime
, bool* isUTC
,
3275 bool* hasOffset
, int64_t* timeZoneOffset
,
3276 MutableHandle
<ParsedTimeZone
> timeZoneName
,
3277 MutableHandle
<JSString
*> calendar
) {
3278 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
3284 auto parseResult
= ::ParseTemporalZonedDateTimeString(linear
);
3285 if (parseResult
.isErr()) {
3286 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3287 parseResult
.unwrapErr());
3290 ZonedDateTimeString parsed
= parseResult
.unwrap();
3292 // Step 2. (ParseISODateTime, steps 1-18.)
3293 if (!ParseISODateTime(cx
, parsed
, dateTime
)) {
3297 // Step 2. (ParseISODateTime, steps 19-21.)
3299 MOZ_ASSERT(parsed
.timeZone
.hasAnnotation());
3301 // Case 1: 19700101T00:00Z[+02:00]
3302 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
3304 // Case 2: 19700101T00:00+02:00[+02:00]
3305 // { [[Z]]: false, [[OffsetString]]: "+02:00", [[Name]]: "+02:00" }
3307 // Case 3: 19700101[+02:00]
3308 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
3310 // Case 4: 19700101T00:00Z[Europe/Berlin]
3311 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
3313 // Case 5: 19700101T00:00+01:00[Europe/Berlin]
3314 // { [[Z]]: false, [[OffsetString]]: "+01:00", [[Name]]: "Europe/Berlin" }
3316 // Case 6: 19700101[Europe/Berlin]
3317 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
3319 const auto& annotation
= parsed
.timeZone
.annotation
;
3320 if (!ParseTimeZoneAnnotation(cx
, annotation
, linear
, timeZoneName
)) {
3324 if (parsed
.timeZone
.isUTC()) {
3327 *timeZoneOffset
= 0;
3328 } else if (parsed
.timeZone
.hasOffset()) {
3331 *timeZoneOffset
= ParseDateTimeUTCOffset(parsed
.timeZone
.offset
);
3335 *timeZoneOffset
= 0;
3339 // Step 2. (ParseISODateTime, steps 23-24.)
3340 if (parsed
.calendar
.present()) {
3341 calendar
.set(ToString(cx
, linear
, parsed
.calendar
));
3347 // Step 2. (ParseISODateTime, step 25.)
3352 * ParseTemporalRelativeToString ( isoString )
3354 template <typename CharT
>
3355 static auto ParseTemporalRelativeToString(mozilla::Span
<const CharT
> str
) {
3356 TemporalParser
<CharT
> parser(str
);
3357 return parser
.parseTemporalDateTimeString();
3361 * ParseTemporalRelativeToString ( isoString )
3363 static auto ParseTemporalRelativeToString(Handle
<JSLinearString
*> str
) {
3364 JS::AutoCheckCannotGC nogc
;
3365 if (str
->hasLatin1Chars()) {
3366 return ParseTemporalRelativeToString
<Latin1Char
>(str
->latin1Range(nogc
));
3368 return ParseTemporalRelativeToString
<char16_t
>(str
->twoByteRange(nogc
));
3372 * ParseTemporalRelativeToString ( isoString )
3374 bool js::temporal::ParseTemporalRelativeToString(
3375 JSContext
* cx
, Handle
<JSString
*> str
, PlainDateTime
* dateTime
, bool* isUTC
,
3376 bool* hasOffset
, int64_t* timeZoneOffset
,
3377 MutableHandle
<ParsedTimeZone
> timeZoneName
,
3378 MutableHandle
<JSString
*> calendar
) {
3379 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
3385 auto parseResult
= ::ParseTemporalRelativeToString(linear
);
3386 if (parseResult
.isErr()) {
3387 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3388 parseResult
.unwrapErr());
3391 ZonedDateTimeString parsed
= parseResult
.unwrap();
3394 if (parsed
.timeZone
.isUTC() && !parsed
.timeZone
.hasAnnotation()) {
3395 JS_ReportErrorNumberASCII(
3396 cx
, GetErrorMessage
, nullptr,
3397 JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR_WITHOUT_NAME
);
3401 // Step 4. (ParseISODateTime, steps 1-18.)
3402 if (!ParseISODateTime(cx
, parsed
, dateTime
)) {
3406 // Step 4. (ParseISODateTime, steps 19-22.)
3407 if (parsed
.timeZone
.hasAnnotation()) {
3408 // Case 1: 19700101Z[+02:00]
3409 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
3411 // Case 2: 19700101+00:00[+02:00]
3412 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
3414 // Case 3: 19700101[+02:00]
3415 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
3417 // Case 4: 19700101Z[Europe/Berlin]
3418 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
3420 // Case 5: 19700101+00:00[Europe/Berlin]
3421 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
3423 // Case 6: 19700101[Europe/Berlin]
3424 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
3426 const auto& annotation
= parsed
.timeZone
.annotation
;
3427 if (!ParseTimeZoneAnnotation(cx
, annotation
, linear
, timeZoneName
)) {
3431 if (parsed
.timeZone
.isUTC()) {
3434 *timeZoneOffset
= 0;
3435 } else if (parsed
.timeZone
.hasOffset()) {
3438 *timeZoneOffset
= ParseDateTimeUTCOffset(parsed
.timeZone
.offset
);
3442 *timeZoneOffset
= 0;
3445 // ToRelativeTemporalObject ignores any other time zone information when no
3446 // bracketed time zone annotation is present.
3450 *timeZoneOffset
= 0;
3451 timeZoneName
.set(ParsedTimeZone
{});
3454 // Step 4. (ParseISODateTime, steps 23-24.)
3455 if (parsed
.calendar
.present()) {
3456 calendar
.set(ToString(cx
, linear
, parsed
.calendar
));
3466 void js::temporal::ParsedTimeZone::trace(JSTracer
* trc
) {
3467 TraceNullableRoot(trc
, &name
, "ParsedTimeZone::name");