Skip to content

Commit 278a5fb

Browse files
committed
Don't coerce non-String inputs to strings
This commit stops converting non-string inputs to strings when parsing ISO strings or property bag fields. When numbers are coerced to strings, the result is sometimes a valid ISO string subset but it often doesn't behave as expected. The result is often brittle, yielding "data driven exceptions" that we've tried to avoid. Numbers can also be ambiguous, e.g. is a Number passed for `offset` mean hours like an ISO string, minutes like Date.p.getTimezoneOffset, or msecs like Date.p.getTime(). This commit also removes coercing objects to strings in the same contexts (like TimeZone and Calendar constructors) because it's unclear that there are use cases for this coercion.
1 parent 08d214e commit 278a5fb

15 files changed

+87
-70
lines changed

Diff for: polyfill/lib/calendar.mjs

+4-10
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,14 @@ const impl = {};
4141

4242
export class Calendar {
4343
constructor(id) {
44-
// Note: if the argument is not passed, IsBuiltinCalendar("undefined") will fail. This check
45-
// exists only to improve the error message.
46-
if (arguments.length < 1) {
47-
throw new RangeError('missing argument: id is required');
48-
}
49-
50-
id = ES.ToString(id);
51-
if (!ES.IsBuiltinCalendar(id)) throw new RangeError(`invalid calendar identifier ${id}`);
44+
const stringId = ES.RequireString(id);
45+
if (!ES.IsBuiltinCalendar(stringId)) throw new RangeError(`invalid calendar identifier ${stringId}`);
5246
CreateSlots(this);
53-
SetSlot(this, CALENDAR_ID, ES.ASCIILowercase(id));
47+
SetSlot(this, CALENDAR_ID, ES.ASCIILowercase(stringId));
5448

5549
if (typeof __debug__ !== 'undefined' && __debug__) {
5650
Object.defineProperty(this, '_repr_', {
57-
value: `${this[Symbol.toStringTag]} <${id}>`,
51+
value: `${this[Symbol.toStringTag]} <${stringId}>`,
5852
writable: false,
5953
enumerable: false,
6054
configurable: false

Diff for: polyfill/lib/ecmascript.mjs

+33-14
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const ObjectCreate = Object.create;
1818
const ObjectDefineProperty = Object.defineProperty;
1919
const ObjectEntries = Object.entries;
2020
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
21+
const StringCtor = String;
2122
const StringFromCharCode = String.fromCharCode;
2223
const StringPrototypeCharCodeAt = String.prototype.charCodeAt;
2324
const StringPrototypeMatchAll = String.prototype.matchAll;
@@ -146,10 +147,27 @@ export function ToIntegerIfIntegral(value) {
146147
return number;
147148
}
148149

150+
// This convenience function isn't in the spec, but is useful in the polyfill
151+
// for DRY and better error messages.
152+
export function RequireString(value) {
153+
if (Type(value) !== 'String') {
154+
// Use String() to ensure that Symbols won't throw
155+
throw new TypeError(`expected a string, not ${StringCtor(value)}`);
156+
}
157+
return value;
158+
}
159+
160+
// This function is an enum in the spec, but it's helpful to make it a
161+
// function in the polyfill.
162+
function ToPrimitiveAndRequireString(value) {
163+
value = ToPrimitive(value, StringCtor);
164+
return RequireString(value);
165+
}
166+
149167
const BUILTIN_CASTS = new Map([
150168
['year', ToIntegerWithTruncation],
151169
['month', ToPositiveIntegerWithTruncation],
152-
['monthCode', ToString],
170+
['monthCode', ToPrimitiveAndRequireString],
153171
['day', ToPositiveIntegerWithTruncation],
154172
['hour', ToIntegerWithTruncation],
155173
['minute', ToIntegerWithTruncation],
@@ -167,9 +185,9 @@ const BUILTIN_CASTS = new Map([
167185
['milliseconds', ToIntegerIfIntegral],
168186
['microseconds', ToIntegerIfIntegral],
169187
['nanoseconds', ToIntegerIfIntegral],
170-
['era', ToString],
188+
['era', ToPrimitiveAndRequireString],
171189
['eraYear', ToIntegerOrInfinity],
172-
['offset', ToString]
190+
['offset', ToPrimitiveAndRequireString]
173191
]);
174192

175193
const BUILTIN_DEFAULTS = new Map([
@@ -704,7 +722,7 @@ export function RegulateISOYearMonth(year, month, overflow) {
704722

705723
export function ToTemporalDurationRecord(item) {
706724
if (Type(item) !== 'Object') {
707-
return ParseTemporalDurationString(ToString(item));
725+
return ParseTemporalDurationString(RequireString(item));
708726
}
709727
if (IsTemporalDuration(item)) {
710728
return {
@@ -986,7 +1004,7 @@ export function ToRelativeTemporalObject(options) {
9861004
} else {
9871005
let tzName, z;
9881006
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, tzName, offset, z } =
989-
ParseISODateTime(ToString(relativeTo)));
1007+
ParseISODateTime(RequireString(relativeTo)));
9901008
if (tzName) {
9911009
timeZone = ToTemporalTimeZoneSlotValue(tzName);
9921010
if (z) {
@@ -1134,7 +1152,7 @@ export function ToTemporalDate(item, options) {
11341152
return CalendarDateFromFields(calendar, fields, options);
11351153
}
11361154
ToTemporalOverflow(options); // validate and ignore
1137-
let { year, month, day, calendar, z } = ParseTemporalDateString(ToString(item));
1155+
let { year, month, day, calendar, z } = ParseTemporalDateString(RequireString(item));
11381156
if (z) throw new RangeError('Z designator not supported for PlainDate');
11391157
if (!calendar) calendar = 'iso8601';
11401158
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
@@ -1208,7 +1226,7 @@ export function ToTemporalDateTime(item, options) {
12081226
ToTemporalOverflow(options); // validate and ignore
12091227
let z;
12101228
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, z } =
1211-
ParseTemporalDateTimeString(ToString(item)));
1229+
ParseTemporalDateTimeString(RequireString(item)));
12121230
if (z) throw new RangeError('Z designator not supported for PlainDateTime');
12131231
RejectDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
12141232
if (!calendar) calendar = 'iso8601';
@@ -1243,7 +1261,8 @@ export function ToTemporalInstant(item) {
12431261
const TemporalInstant = GetIntrinsic('%Temporal.Instant%');
12441262
return new TemporalInstant(GetSlot(item, EPOCHNANOSECONDS));
12451263
}
1246-
const ns = ParseTemporalInstant(ToString(item));
1264+
item = ToPrimitive(item, StringCtor);
1265+
const ns = ParseTemporalInstant(RequireString(item));
12471266
const TemporalInstant = GetIntrinsic('%Temporal.Instant%');
12481267
return new TemporalInstant(ns);
12491268
}
@@ -1273,7 +1292,7 @@ export function ToTemporalMonthDay(item, options) {
12731292
}
12741293

12751294
ToTemporalOverflow(options); // validate and ignore
1276-
let { month, day, referenceISOYear, calendar } = ParseTemporalMonthDayString(ToString(item));
1295+
let { month, day, referenceISOYear, calendar } = ParseTemporalMonthDayString(RequireString(item));
12771296
if (calendar === undefined) calendar = 'iso8601';
12781297
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
12791298
calendar = ASCIILowercase(calendar);
@@ -1315,7 +1334,7 @@ export function ToTemporalTime(item, overflow = 'constrain') {
13151334
overflow
13161335
));
13171336
} else {
1318-
({ hour, minute, second, millisecond, microsecond, nanosecond } = ParseTemporalTimeString(ToString(item)));
1337+
({ hour, minute, second, millisecond, microsecond, nanosecond } = ParseTemporalTimeString(RequireString(item)));
13191338
RejectTime(hour, minute, second, millisecond, microsecond, nanosecond);
13201339
}
13211340
const TemporalPlainTime = GetIntrinsic('%Temporal.PlainTime%');
@@ -1332,7 +1351,7 @@ export function ToTemporalYearMonth(item, options) {
13321351
}
13331352

13341353
ToTemporalOverflow(options); // validate and ignore
1335-
let { year, month, referenceISODay, calendar } = ParseTemporalYearMonthString(ToString(item));
1354+
let { year, month, referenceISODay, calendar } = ParseTemporalYearMonthString(RequireString(item));
13361355
if (calendar === undefined) calendar = 'iso8601';
13371356
if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`);
13381357
calendar = ASCIILowercase(calendar);
@@ -1453,7 +1472,7 @@ export function ToTemporalZonedDateTime(item, options) {
14531472
} else {
14541473
let tzName, z;
14551474
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, tzName, offset, z, calendar } =
1456-
ParseTemporalZonedDateTimeString(ToString(item)));
1475+
ParseTemporalZonedDateTimeString(RequireString(item)));
14571476
timeZone = ToTemporalTimeZoneSlotValue(tzName);
14581477
if (z) {
14591478
offsetBehaviour = 'exact';
@@ -1979,7 +1998,7 @@ export function ToTemporalCalendarSlotValue(calendarLike) {
19791998
}
19801999
return calendarLike;
19812000
}
1982-
const identifier = ToString(calendarLike);
2001+
const identifier = RequireString(calendarLike);
19832002
if (IsBuiltinCalendar(identifier)) return ASCIILowercase(identifier);
19842003
let calendar;
19852004
try {
@@ -2098,7 +2117,7 @@ export function ToTemporalTimeZoneSlotValue(temporalTimeZoneLike) {
20982117
}
20992118
return temporalTimeZoneLike;
21002119
}
2101-
const identifier = ToString(temporalTimeZoneLike);
2120+
const identifier = RequireString(temporalTimeZoneLike);
21022121
return ParseTemporalTimeZone(identifier);
21032122
}
21042123

Diff for: polyfill/lib/timezone.mjs

+2-7
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,8 @@ import {
2020
} from './slots.mjs';
2121

2222
export class TimeZone {
23-
constructor(identifier) {
24-
// Note: if the argument is not passed, GetCanonicalTimeZoneIdentifier(undefined) will throw.
25-
// This check exists only to improve the error message.
26-
if (arguments.length < 1) {
27-
throw new RangeError('missing argument: identifier is required');
28-
}
29-
let stringIdentifier = ES.ToString(identifier);
23+
constructor(id) {
24+
let stringIdentifier = ES.RequireString(id);
3025
if (ES.IsTimeZoneOffsetString(stringIdentifier)) {
3126
stringIdentifier = ES.CanonicalizeTimeZoneOffsetString(stringIdentifier);
3227
} else {

Diff for: polyfill/test262

Submodule test262 updated 734 files

Diff for: spec/abstractops.html

+23-16
Original file line numberDiff line numberDiff line change
@@ -567,16 +567,21 @@ <h1>
567567
</emu-table>
568568
</emu-clause>
569569

570-
<emu-clause id="sec-temporal-torelativetemporalobject" aoid="ToRelativeTemporalObject">
571-
<h1>ToRelativeTemporalObject ( _options_ )</h1>
572-
<p>
573-
The abstract operation ToRelativeTemporalObject examines the value of the `relativeTo` property of its _options_ argument.
574-
If this is not present, it returns *undefined*.
575-
Otherwise, it attempts to return a Temporal.ZonedDateTime instance or Temporal.PlainDate instance, in order of preference, by converting the value.
576-
If neither of those are possible, the operation throws a *RangeError*.
577-
</p>
570+
<emu-clause id="sec-temporal-torelativetemporalobject" type="abstract operation">
571+
<h1>ToRelativeTemporalObject (
572+
_options_: an Object
573+
): either a normal completion containing either a Temporal.ZonedDateTime object or a Temporal.PlainDate object, or a throw completion</h1>
574+
<dl class="header">
575+
<dt>description</dt>
576+
<dd>
577+
It examines the value of the `relativeTo` property of its _options_ argument.
578+
If the value is *undefined*, it returns *undefined*.
579+
If the value is not a String or an Object, it throws a *TypeError*.
580+
Otherwise, it attempts to return a Temporal.ZonedDateTime instance or Temporal.PlainDate instance, in order of preference, by converting the value.
581+
If neither of those are possible, it throws a *RangeError*.
582+
</dd>
583+
</dl>
578584
<emu-alg>
579-
1. Assert: Type(_options_) is Object.
580585
1. Let _value_ be ? Get(_options_, *"relativeTo"*).
581586
1. If _value_ is *undefined*, then
582587
1. Return _value_.
@@ -602,8 +607,8 @@ <h1>ToRelativeTemporalObject ( _options_ )</h1>
602607
1. If _offsetString_ is *undefined*, then
603608
1. Set _offsetBehaviour_ to ~wall~.
604609
1. Else,
605-
1. Let _string_ be ? ToString(_value_).
606-
1. Let _result_ be ? ParseTemporalRelativeToString(_string_).
610+
1. If _value_ is not a String, throw a *TypeError* exception.
611+
1. Let _result_ be ? ParseTemporalRelativeToString(_value_).
607612
1. Let _offsetString_ be _result_.[[TimeZone]].[[OffsetString]].
608613
1. Let _timeZoneName_ be _result_.[[TimeZone]].[[Name]].
609614
1. If _timeZoneName_ is *undefined*, then
@@ -1807,8 +1812,10 @@ <h1>
18071812
1. Set _value_ to ? ToPositiveIntegerWithTruncation(_value_).
18081813
1. Set _value_ to 𝔽(_value_).
18091814
1. Else,
1810-
1. Assert: _Conversion_ is ~ToString~.
1811-
1. Set _value_ to ? ToString(_value_).
1815+
1. Assert: _Conversion_ is ~ToPrimitiveAndRequireString~.
1816+
1. NOTE: Non-primitive values are supported here for consistency with other fields, but such values must coerce to Strings.
1817+
1. Set _value_ to ? ToPrimitive(_value_, ~string~).
1818+
1. If _value_ is not a String, throw a *TypeError* exception.
18121819
1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
18131820
1. Else if _requiredFields_ is a List, then
18141821
1. If _requiredFields_ contains _property_, then
@@ -1843,7 +1850,7 @@ <h1>
18431850
</tr>
18441851
<tr>
18451852
<td>*"monthCode"*</td>
1846-
<td>~ToString~</td>
1853+
<td>~ToPrimitiveAndRequireString~</td>
18471854
<td>*undefined*</td>
18481855
</tr>
18491856
<tr>
@@ -1883,12 +1890,12 @@ <h1>
18831890
</tr>
18841891
<tr>
18851892
<td>*"offset"*</td>
1886-
<td>~ToString~</td>
1893+
<td>~ToPrimitiveAndRequireString~</td>
18871894
<td>*undefined*</td>
18881895
</tr>
18891896
<tr>
18901897
<td>*"era"*</td>
1891-
<td>~ToString~</td>
1898+
<td>~ToPrimitiveAndRequireString~</td>
18921899
<td>*undefined*</td>
18931900
</tr>
18941901
<tr>

Diff for: spec/calendar.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,8 @@ <h1>
525525
1. Return _temporalCalendarLike_.[[Calendar]].
526526
1. If ? ObjectImplementsTemporalCalendarProtocol(_temporalCalendarLike_) is *false*, throw a *TypeError* exception.
527527
1. Return _temporalCalendarLike_.
528-
1. Let _identifier_ be ? ToString(_temporalCalendarLike_).
529-
1. Set _identifier_ to ? ParseTemporalCalendarString(_identifier_).
528+
1. If _temporalCalendarLike_ is not a String, throw a *TypeError* exception.
529+
1. Let _identifier_ be ? ParseTemporalCalendarString(_temporalCalendarLike_).
530530
1. If IsBuiltinCalendar(_identifier_) is *false*, throw a *RangeError* exception.
531531
1. Return the ASCII-lowercase of _identifier_.
532532
</emu-alg>
@@ -1020,7 +1020,7 @@ <h1>Temporal.Calendar ( _id_ )</h1>
10201020
<emu-alg>
10211021
1. If NewTarget is *undefined*, then
10221022
1. Throw a *TypeError* exception.
1023-
1. Set _id_ to ? ToString(_id_).
1023+
1. If _id_ is not a String, throw a *TypeError* exception.
10241024
1. If IsBuiltinCalendar(_id_) is *false*, then
10251025
1. Throw a *RangeError* exception.
10261026
1. Return ? CreateTemporalCalendar(_id_, NewTarget).

Diff for: spec/duration.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -932,8 +932,8 @@ <h1>
932932
</dl>
933933
<emu-alg>
934934
1. If Type(_temporalDurationLike_) is not Object, then
935-
1. Let _string_ be ? ToString(_temporalDurationLike_).
936-
1. Return ? ParseTemporalDurationString(_string_).
935+
1. If _temporalDurationLike_ is not a String, throw a *TypeError* exception.
936+
1. Return ? ParseTemporalDurationString(_temporalDurationLike_).
937937
1. If _temporalDurationLike_ has an [[InitializedTemporalDuration]] internal slot, then
938938
1. Return ! CreateDurationRecord(_temporalDurationLike_.[[Years]], _temporalDurationLike_.[[Months]], _temporalDurationLike_.[[Weeks]], _temporalDurationLike_.[[Days]], _temporalDurationLike_.[[Hours]], _temporalDurationLike_.[[Minutes]], _temporalDurationLike_.[[Seconds]], _temporalDurationLike_.[[Milliseconds]], _temporalDurationLike_.[[Microseconds]], _temporalDurationLike_.[[Nanoseconds]]).
939939
1. Let _result_ be a new Duration Record with each field set to 0.

Diff for: spec/instant.html

+4-2
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,10 @@ <h1>ToTemporalInstant ( _item_ )</h1>
518518
1. Return _item_.
519519
1. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
520520
1. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
521-
1. Let _string_ be ? ToString(_item_).
522-
1. Let _epochNanoseconds_ be ? ParseTemporalInstant(_string_).
521+
1. NOTE: This use of ToPrimitive allows Instant-like objects to be converted.
522+
1. Set _item_ to ? ToPrimitive(_item_, ~string~).
523+
1. If _item_ is not a String, throw a *TypeError* exception.
524+
1. Let _epochNanoseconds_ be ? ParseTemporalInstant(_item_).
523525
1. Return ! CreateTemporalInstant(_epochNanoseconds_).
524526
</emu-alg>
525527
</emu-clause>

Diff for: spec/plaindate.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -756,8 +756,8 @@ <h1>ToTemporalDate ( _item_ [ , _options_ ] )</h1>
756756
1. Let _fields_ be ? PrepareTemporalFields(_item_, _fieldNames_, «»).
757757
1. Return ? CalendarDateFromFields(_calendar_, _fields_, _options_).
758758
1. Perform ? ToTemporalOverflow(_options_).
759-
1. Let _string_ be ? ToString(_item_).
760-
1. Let _result_ be ? ParseTemporalDateString(_string_).
759+
1. If _item_ is not a String, throw a *TypeError* exception.
760+
1. Let _result_ be ? ParseTemporalDateString(_item_).
761761
1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*.
762762
1. Let _calendar_ be _result_.[[Calendar]].
763763
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.

Diff for: spec/plaindatetime.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -916,8 +916,8 @@ <h1>ToTemporalDateTime ( _item_ [ , _options_ ] )</h1>
916916
1. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
917917
1. Else,
918918
1. Perform ? ToTemporalOverflow(_options_).
919-
1. Let _string_ be ? ToString(_item_).
920-
1. Let _result_ be ? ParseTemporalDateTimeString(_string_).
919+
1. If _item_ is not a String, throw a *TypeError* exception.
920+
1. Let _result_ be ? ParseTemporalDateTimeString(_item_).
921921
1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*.
922922
1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*.
923923
1. Let _calendar_ be _result_.[[Calendar]].

Diff for: spec/plainmonthday.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,8 @@ <h1>ToTemporalMonthDay ( _item_ [ , _options_ ] )</h1>
383383
1. Perform ! CreateDataPropertyOrThrow(_fields_, *"year"*, 𝔽(_referenceISOYear_)).
384384
1. Return ? CalendarMonthDayFromFields(_calendar_, _fields_, _options_).
385385
1. Perform ? ToTemporalOverflow(_options_).
386-
1. Let _string_ be ? ToString(_item_).
387-
1. Let _result_ be ? ParseTemporalMonthDayString(_string_).
386+
1. If _item_ is not a String, throw a *TypeError* exception.
387+
1. Let _result_ be ? ParseTemporalMonthDayString(_item_).
388388
1. Let _calendar_ be _result_.[[Calendar]].
389389
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
390390
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.

Diff for: spec/plaintime.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,8 @@ <h1>ToTemporalTime ( _item_ [ , _overflow_ ] )</h1>
580580
1. Let _result_ be ? ToTemporalTimeRecord(_item_).
581581
1. Set _result_ to ? RegulateTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _overflow_).
582582
1. Else,
583-
1. Let _string_ be ? ToString(_item_).
584-
1. Let _result_ be ? ParseTemporalTimeString(_string_).
583+
1. If _item_ is not a String, throw a *TypeError* exception.
584+
1. Let _result_ be ? ParseTemporalTimeString(_item_).
585585
1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*.
586586
1. Return ! CreateTemporalTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]).
587587
</emu-alg>

Diff for: spec/plainyearmonth.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,8 @@ <h1>ToTemporalYearMonth ( _item_ [ , _options_ ] )</h1>
497497
1. Let _fields_ be ? PrepareTemporalFields(_item_, _fieldNames_, «»).
498498
1. Return ? CalendarYearMonthFromFields(_calendar_, _fields_, _options_).
499499
1. Perform ? ToTemporalOverflow(_options_).
500-
1. Let _string_ be ? ToString(_item_).
501-
1. Let _result_ be ? ParseTemporalYearMonthString(_string_).
500+
1. If _item_ is not a String, throw a *TypeError* exception.
501+
1. Let _result_ be ? ParseTemporalYearMonthString(_item_).
502502
1. Let _calendar_ be _result_.[[Calendar]].
503503
1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*.
504504
1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception.

0 commit comments

Comments
 (0)