From 80dd0718544f1dae6fe61ee2be81609544cb7319 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 30 Sep 2024 17:42:18 -0700 Subject: [PATCH] Editorial: Adjust RangeError condition in GetPossibleEpochNanoseconds Similarly to the previous commit, 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). Previously to PR #2925, that could not happen because GetPossibleInstantsFor took a PlainDateTime object, but now it takes an ISO Date-Time Record. Note that this is editorial, because IsValidEpochNanoseconds would throw anyway in this case even if GetUTCEpochNanoseconds was fully defined for large inputs. --- polyfill/lib/ecmascript.mjs | 36 +++++++++++++++++++++++++++++++----- spec/timezone.html | 6 ++++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 8619a96b9..d0b7f3cb3 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -2098,13 +2098,38 @@ export function GetPossibleEpochNanoseconds(timeZone, isoDateTime) { const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = isoDateTime; const offsetMinutes = ParseTimeZoneIdentifier(timeZone).offsetMinutes; if (offsetMinutes !== undefined) { - return [ - GetUTCEpochNanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond).subtract( - offsetMinutes * 60e9 - ) - ]; + const balanced = BalanceISODateTime( + year, + month, + day, + hour, + minute - offsetMinutes, + second, + millisecond, + microsecond, + nanosecond + ); + if (MathAbs(ISODateToEpochDays(balanced.year, balanced.month - 1, balanced.day)) > 1e8) { + throw new RangeErrorCtor('date/time value is outside the supported range'); + } + const epochNs = GetUTCEpochNanoseconds( + balanced.year, + balanced.month, + balanced.day, + balanced.hour, + balanced.minute, + balanced.second, + balanced.millisecond, + balanced.microsecond, + balanced.nanosecond + ); + ValidateEpochNanoseconds(epochNs); + return [epochNs]; } + if (MathAbs(ISODateToEpochDays(year, month - 1, day)) > 1e8) { + throw new RangeErrorCtor('date/time value is outside the supported range'); + } return GetNamedTimeZoneEpochNanoseconds( timeZone, year, @@ -2693,6 +2718,7 @@ export function GetNamedTimeZoneEpochNanoseconds( ) { return undefined; } + ValidateEpochNanoseconds(epochNanoseconds); return epochNanoseconds; } ]); diff --git a/spec/timezone.html b/spec/timezone.html index 07dbb5f9f..c22315fd9 100644 --- a/spec/timezone.html +++ b/spec/timezone.html @@ -357,10 +357,12 @@

1. Let _parseResult_ be ! ParseTimeZoneIdentifier(_timeZone_). 1. If _parseResult_.[[OffsetMinutes]] is not ~empty~, then - 1. Let _offsetNanoseconds_ be _parseResult_.[[OffsetMinutes]] × (60 × 109). - 1. Let _epochNanoseconds_ be GetUTCEpochNanoseconds(_isoDateTime_.[[Year]], _isoDateTime_.[[Month]], _isoDateTime_.[[Day]], _isoDateTime_.[[Hour]], _isoDateTime_.[[Minute]], _isoDateTime_.[[Second]], _isoDateTime_.[[Millisecond]], _isoDateTime_.[[Microsecond]], _isoDateTime_.[[Nanosecond]]) - ℤ(_offsetNanoseconds_). + 1. Let _balanced_ be BalanceISODateTime(_isoDateTime_.[[Year]], _isoDateTime_.[[Month]], _isoDateTime_.[[Day]], _isoDateTime_.[[Hour]], _isoDateTime_.[[Minute]] - _parseResult_.[[OffsetMinutes]], _isoDateTime_.[[Second]], _isoDateTime_.[[Millisecond]], _isoDateTime_.[[Microsecond]], _isoDateTime_.[[Nanosecond]]). + 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. Let _possibleEpochNanoseconds_ be « _epochNanoseconds_ ». 1. Else, + 1. If abs(ISODateToEpochDays(_isoDateTime_.[[Year]], _isoDateTime_.[[Month]] - 1, _isoDateTime_.[[Day]])) > 108, throw a *RangeError* exception. 1. Let _possibleEpochNanoseconds_ be GetNamedTimeZoneEpochNanoseconds(_parseResult_.[[Name]], _isoDateTime_.[[Year]], _isoDateTime_.[[Month]], _isoDateTime_.[[Day]], _isoDateTime_.[[Hour]], _isoDateTime_.[[Minute]], _isoDateTime_.[[Second]], _isoDateTime_.[[Millisecond]], _isoDateTime_.[[Microsecond]], _isoDateTime_.[[Nanosecond]]). 1. For each value _epochNanoseconds_ in _possibleEpochNanoseconds_, do 1. If IsValidEpochNanoseconds(_epochNanoseconds_) is *false*, throw a *RangeError* exception.