Skip to content

Commit

Permalink
Fix: Fix calendar validation in ToTemporal___ operations
Browse files Browse the repository at this point in the history
In ToRelativeTemporalObject, ToTemporalDateTime, ToTemporalMonthDay,
ToTemporalYearMonth, and ToTemporalZonedDateTime, PR #2482 mistakenly left
out the validation of the calendar name when parsing a string.

The validation in ToTemporalDate was incorrect, because it should not have
accepted ISO strings as calendar identifiers, which
ToTemporalCalendarSlotValue would have, e.g.:

    Temporal.PlainDate.from('2023-04-13[u-ca=2023-04-13]')

(would return a PlainDate with the iso8601 calendar, instead of throwing)

Closes: #2546
  • Loading branch information
ptomato committed Apr 18, 2023
1 parent 43c6be1 commit f3c73dd
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 11 deletions.
23 changes: 15 additions & 8 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,8 @@ export function ToRelativeTemporalObject(options) {
);
}
if (!calendar) calendar = 'iso8601';
calendar = ToTemporalCalendarSlotValue(calendar);
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);
}
if (timeZone === undefined) return CreateTemporalDate(year, month, day, calendar);
const offsetNs = offsetBehaviour === 'option' ? ParseTimeZoneOffsetString(offset) : 0;
Expand Down Expand Up @@ -1127,8 +1128,10 @@ export function ToTemporalDate(item, options) {
ToTemporalOverflow(options); // validate and ignore
let { year, month, day, calendar, z } = ParseTemporalDateString(ToString(item));
if (z) throw new RangeError('Z designator not supported for PlainDate');
const TemporalPlainDate = GetIntrinsic('%Temporal.PlainDate%');
return new TemporalPlainDate(year, month, day, calendar); // include validation
if (!calendar) calendar = 'iso8601';
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);
return CreateTemporalDate(year, month, day, calendar);
}

export function InterpretTemporalDateTimeFields(calendar, fields, options) {
Expand Down Expand Up @@ -1200,8 +1203,9 @@ export function ToTemporalDateTime(item, options) {
ParseTemporalDateTimeString(ToString(item)));
if (z) throw new RangeError('Z designator not supported for PlainDateTime');
RejectDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
if (calendar === undefined) calendar = 'iso8601';
calendar = ToTemporalCalendarSlotValue(calendar);
if (!calendar) calendar = 'iso8601';
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);
}
return CreateTemporalDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar);
}
Expand Down Expand Up @@ -1263,7 +1267,8 @@ export function ToTemporalMonthDay(item, options) {
ToTemporalOverflow(options); // validate and ignore
let { month, day, referenceISOYear, calendar } = ParseTemporalMonthDayString(ToString(item));
if (calendar === undefined) calendar = 'iso8601';
calendar = ToTemporalCalendarSlotValue(calendar);
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);

if (referenceISOYear === undefined) {
RejectISODate(1972, month, day);
Expand Down Expand Up @@ -1321,7 +1326,8 @@ export function ToTemporalYearMonth(item, options) {
ToTemporalOverflow(options); // validate and ignore
let { year, month, referenceISODay, calendar } = ParseTemporalYearMonthString(ToString(item));
if (calendar === undefined) calendar = 'iso8601';
calendar = ToTemporalCalendarSlotValue(calendar);
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);

if (referenceISODay === undefined) {
RejectISODate(year, month, 1);
Expand Down Expand Up @@ -1447,7 +1453,8 @@ export function ToTemporalZonedDateTime(item, options) {
offsetBehaviour = 'wall';
}
if (!calendar) calendar = 'iso8601';
calendar = ToTemporalCalendarSlotValue(calendar);
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
calendar = ASCIILowercase(calendar);
matchMinute = true; // ISO strings may specify offset with less precision
disambiguation = ToTemporalDisambiguation(options);
offsetOpt = ToTemporalOffset(options, 'reject');
Expand Down
6 changes: 4 additions & 2 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,6 @@ <h1>ToRelativeTemporalObject ( _options_ )</h1>
1. Else,
1. Let _string_ be ? ToString(_value_).
1. Let _result_ be ? ParseTemporalRelativeToString(_string_).
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. Let _offsetString_ be _result_.[[TimeZone]].[[OffsetString]].
1. Let _timeZoneName_ be _result_.[[TimeZone]].[[Name]].
1. If _timeZoneName_ is *undefined*, then
Expand All @@ -514,6 +512,10 @@ <h1>ToRelativeTemporalObject ( _options_ )</h1>
1. Else if _offsetString_ is *undefined*, then
1. Set _offsetBehaviour_ to ~wall~.
1. Set _matchBehaviour_ to ~match minutes~.
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. If _timeZone_ is *undefined*, then
1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendar_).
1. If _offsetBehaviour_ is ~option~, then
Expand Down
5 changes: 4 additions & 1 deletion spec/plaindate.html
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,10 @@ <h1>ToTemporalDate ( _item_ [ , _options_ ] )</h1>
1. Let _string_ be ? ToString(_item_).
1. Let _result_ be ? ParseTemporalDateString(_string_).
1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*.
1. Let _calendar_ be ? ToTemporalCalendarSlotValue(_result_.[[Calendar]], *"iso8601"*).
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendar_).
</emu-alg>
</emu-clause>
Expand Down
2 changes: 2 additions & 0 deletions spec/plaindatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,8 @@ <h1>ToTemporalDateTime ( _item_ [ , _options_ ] )</h1>
1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*.
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. Return ? CreateTemporalDateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _calendar_).
</emu-alg>
</emu-clause>
Expand Down
2 changes: 2 additions & 0 deletions spec/plainmonthday.html
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ <h1>ToTemporalMonthDay ( _item_ [ , _options_ ] )</h1>
1. Let _result_ be ? ParseTemporalMonthDayString(_string_).
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. If _result_.[[Year]] is *undefined*, then
1. Return ? CreateTemporalMonthDay(_result_.[[Month]], _result_.[[Day]], _calendar_, _referenceISOYear_).
1. Set _result_ to ? CreateTemporalMonthDay(_result_.[[Month]], _result_.[[Day]], _calendar_, _referenceISOYear_).
Expand Down
2 changes: 2 additions & 0 deletions spec/plainyearmonth.html
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ <h1>ToTemporalYearMonth ( _item_ [ , _options_ ] )</h1>
1. Let _result_ be ? ParseTemporalYearMonthString(_string_).
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. Set _result_ to ? CreateTemporalYearMonth(_result_.[[Year]], _result_.[[Month]], _calendar_, _result_.[[Day]]).
1. NOTE: The following operation is called without _options_, in order for the calendar to store a canonical value in the [[ISODay]] internal slot of the result.
1. Return ? CalendarYearMonthFromFields(_calendar_, _result_).
Expand Down
2 changes: 2 additions & 0 deletions spec/zoneddatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,8 @@ <h1>
1. Set _offsetBehaviour_ to ~wall~.
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.
1. Set _calendar_ to the ASCII-lowercase of _calendar_.
1. Set _matchBehaviour_ to ~match minutes~.
1. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
1. Let _offsetOption_ be ? ToTemporalOffset(_options_, *"reject"*).
Expand Down

0 comments on commit f3c73dd

Please sign in to comment.