From 43bc062894916befe401ef413e78029f5e41edf3 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 30 Sep 2024 13:09:34 -0700 Subject: [PATCH] Editorial: Adjust RangeError condition in ToTemporalInstant We must avoid calling GetUTCEpochNanoseconds with dates that are too large because it is ill-defined in ECMA-262 what is supposed to happen. (See https://github.com/tc39/ecma262/issues/1087). We had a condition in ToTemporalInstant that was supposed to prevent this, but was incorrect because subtracting the UTC offset could push the date out of range. Note that this is editorial, because IsValidEpochNanoseconds would throw anyway in this case even if GetUTCEpochNanoseconds was fully defined for large inputs. See: #2985 --- polyfill/lib/ecmascript.mjs | 20 +++++++++++++++++--- spec/instant.html | 5 +++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 581beeb67..079b56dce 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -1363,7 +1363,7 @@ export function ToTemporalInstant(item) { // ParseTemporalInstantString ensures that either `z` is true or or `offset` is non-undefined const offsetNanoseconds = z ? 0 : ParseDateTimeUTCOffset(offset); - const epochNanoseconds = GetUTCEpochNanoseconds( + const balanced = BalanceISODateTime( year, month, day, @@ -1372,8 +1372,22 @@ export function ToTemporalInstant(item) { second, millisecond, microsecond, - nanosecond - ).subtract(offsetNanoseconds); + nanosecond - offsetNanoseconds + ); + if (MathAbs(ISODateToEpochDays(balanced.year, balanced.month - 1, balanced.day)) > 1e8) { + throw new RangeErrorCtor('date/time value is outside the supported range'); + } + const epochNanoseconds = GetUTCEpochNanoseconds( + balanced.year, + balanced.month, + balanced.day, + balanced.hour, + balanced.minute, + balanced.second, + balanced.millisecond, + balanced.microsecond, + balanced.nanosecond + ); ValidateEpochNanoseconds(epochNanoseconds); return new TemporalInstant(epochNanoseconds); } diff --git a/spec/instant.html b/spec/instant.html index 3ea349fe1..22a91e0b0 100644 --- a/spec/instant.html +++ b/spec/instant.html @@ -412,12 +412,13 @@

1. Let _parsed_ be ? ParseISODateTime(_item_, « |TemporalInstantString| »). 1. Assert: Either _parsed_.[[TimeZone]].[[OffsetString]] is not ~empty~ or _parsed_.[[TimeZone]].[[Z]] is *true*, but not both. 1. If _parsed_.[[TimeZone]].[[Z]] is *true*, let _offsetNanoseconds_ be 0; otherwise, let _offsetNanoseconds_ be ! ParseDateTimeUTCOffset(_parsed_.[[TimeZone]].[[OffsetString]]). - 1. If abs(ISODateToEpochDays(_parsed_.[[Year]], _parsed_.[[Month]] - 1, _parsed_.[[Day]])) > 108, throw a *RangeError* exception. 1. If _parsed_.[[Time]] is ~start-of-day~, then 1. Let _time_ be Time Record { [[Days]]: 0, [[Hour]]: 0, [[Minute]]: 0, [[Second]]: 0, [[Millisecond]]: 0, [[Microsecond]]: 0, [[Nanosecond]]: 0 }. 1. Else, 1. Let _time_ be _parsed_.[[Time]]. - 1. Let _epochNanoseconds_ be GetUTCEpochNanoseconds(_parsed_.[[Year]], _parsed_.[[Month]], _parsed_.[[Day]], _time_.[[Hour]], _time_.[[Minute]], _time_.[[Second]], _time_.[[Millisecond]], _time_.[[Microsecond]], _time_.[[Nanosecond]]) - ℤ(_offsetNanoseconds_). + 1. Let _balanced_ be BalanceISODateTime(_parsed_.[[Year]], _parsed_.[[Month]], _parsed_.[[Day]], _time_.[[Hour]], _time_.[[Minute]], _time_.[[Second]], _time_.[[Millisecond]], _time_.[[Microsecond]], _time_.[[Nanosecond]] - _offsetNanoseconds_). + 1. If abs(ISODateToEpochDays(_balanced_.[[Year]], _balanced_.[[Month]] - 1, _balanced_.[[Day]])) > 108, throw a *RangeError* exception. + 1. Let _epochNanoseconds_ be GetUTCEpochNanoseconds(_balanced_.[[Year]], _balanced_.[[Month]], _balanced_.[[Day]], _balanced_.[[Hour]], _balanced_.[[Minute]], _balanced_.[[Second]], _balanced_.[[Millisecond]], _balanced_.[[Microsecond]], _balanced_.[[Nanosecond]]). 1. If IsValidEpochNanoseconds(_epochNanoseconds_) is *false*, throw a *RangeError* exception. 1. Return ! CreateTemporalInstant(_epochNanoseconds_).