diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index d50ece6df..25bac61fb 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -319,6 +319,41 @@ const monthCodeInfo = { } }; +const calendarMinYear = { + buddhist: -271278, + chinese: -271821, + coptic: -272099, + dangi: -271821, + ethioaa: -266323, + ethiopic: -271823, + gregory: -271821, + hebrew: -268058, + indian: -271899, + 'islamic-civil': -280804, + 'islamic-tbla': -280804, + 'islamic-umalqura': -280804, + japanese: -271821, + persian: -272442, + roc: -273732 +}; +const calendarMaxYear = { + buddhist: 276303, + chinese: 275760, + coptic: 275471, + dangi: 275760, + ethioaa: 281247, + ethiopic: 275747, + gregory: 275760, + hebrew: 279517, + indian: 275682, + 'islamic-civil': 283583, + 'islamic-tbla': 283583, + 'islamic-umalqura': 283583, + japanese: 275760, + persian: 275139, + roc: 273849 +}; + function IsValidMonthCodeForCalendar(calendar, monthCode) { const { monthNumber, isLeapMonth } = ParseMonthCode(monthCode); if (!isLeapMonth && monthNumber >= 1 && monthNumber <= 12) return true; @@ -533,6 +568,7 @@ OneObjectCache.MAX_CACHE_ENTRIES = 1000; * Returns a WeakMap-backed cache that's used to store expensive results * that are associated with a particular ISO Date Record object instance. * + * @param id - calendar ID for the cache * @param obj - object to associate with the cache */ OneObjectCache.getCacheForObject = function (id, obj) { @@ -917,6 +953,10 @@ const nonIsoHelperBase = { // are all present, converting monthCode and eraYear if needed. date = this.adjustCalendarDate(date, cache, overflow, false); + if (date.year > calendarMaxYear[this.id] || date.year < calendarMinYear[this.id]) { + throw new RangeError(`Year ${date.year} out of range for calendar ${this.id}`); + } + // Fix obviously out-of-bounds values. Values that are valid generally, but // not in this particular year, may not be caught here for some calendars. // If so, these will be handled lower below. @@ -1996,7 +2036,7 @@ const helperChinese = ObjectAssign({}, nonIsoHelperBase, { const { month, year } = calendarDate; const previousMonthYear = month > 1 ? year : year - 1; - let previousMonthDate = { year: previousMonthYear, month, day: 1 }; + const previousMonthDate = { year: previousMonthYear, month, day: 1 }; const previousMonth = month > 1 ? month - 1 : this.monthsInYear(previousMonthDate, cache); return this.daysInMonth({ year: previousMonthYear, month: previousMonth }, cache); diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index e5a229c52..6d6a0c3eb 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -770,12 +770,15 @@ export function ToTemporalPartialDurationRecord(temporalDurationLike) { return result; } -export function AdjustDateDurationRecord({ years, months, weeks }, newDays, newWeeks, newMonths) { +export function AdjustDateDurationRecord(dateDuration, newDays, newWeeks, newMonths) { assert(newDays !== undefined, 'days must be provided to AdjustDateDurationRecord'); + const months = newMonths ?? dateDuration.months; + const weeks = newWeeks ?? dateDuration.weeks; + RejectDuration(dateDuration.years, months, weeks, newDays, 0, 0, 0, 0, 0, 0); return { - years, - months: newMonths ?? months, - weeks: newWeeks ?? weeks, + years: dateDuration.years, + months, + weeks, days: newDays }; } @@ -941,10 +944,9 @@ export function ValidateTemporalUnitValue(value, unitGroup, extraValues = []) { if (Call(ArrayPrototypeIncludes, extraValues, [value])) return; for (let index = 0; index < TEMPORAL_UNITS.length; index++) { const unitInfo = TEMPORAL_UNITS[index]; - const singular = unitInfo[0]; const plural = unitInfo[1]; const category = unitInfo[2]; - if (value !== singular && value !== plural) continue; + if (value !== plural) continue; if (unitGroup === 'datetime' || unitGroup === category) return; } throw new RangeErrorCtor(`${value} not allowed as a ${unitGroup} unit`); @@ -2820,7 +2822,7 @@ export function RejectDateTime(year, month, day, hour, minute, second, milliseco export function RejectDateTimeRange(isoDateTime) { const ns = GetUTCEpochNanoseconds(isoDateTime); if (ns.lesser(DATETIME_NS_MIN) || ns.greater(DATETIME_NS_MAX)) { - const dateTimeString = ISODateTimeToString(isoDateTime, 'auto', 'auto', 'never'); + const dateTimeString = ISODateTimeToString(isoDateTime, 'iso8601', 'auto', 'never'); throw new RangeErrorCtor(`${dateTimeString} is outside of supported range`); } } @@ -2830,7 +2832,7 @@ function AssertISODateTimeWithinLimits(isoDateTime) { const ns = GetUTCEpochNanoseconds(isoDateTime); assert( ns.geq(DATETIME_NS_MIN) && ns.leq(DATETIME_NS_MAX), - `${ISODateTimeToString(isoDateTime, 'auto', 'auto', 'never')} is outside the representable range` + `${ISODateTimeToString(isoDateTime, 'iso8601', 'auto', 'never')} is outside the representable range` ); } diff --git a/polyfill/lib/intl.mjs b/polyfill/lib/intl.mjs index 6d4768fb0..0193050fa 100644 --- a/polyfill/lib/intl.mjs +++ b/polyfill/lib/intl.mjs @@ -424,17 +424,15 @@ function formatRange(a, b) { let formatter; let aDayAdjust = 0; let bDayAdjust = 0; - if (isTemporalObject(a) || isTemporalObject(b)) { + if (isFormattableTemporalObject(a) || isFormattableTemporalObject(b)) { if (!sameTemporalType(a, b)) { throw new TypeErrorCtor('Intl.DateTimeFormat.formatRange accepts two values of the same type'); } const aRecord = extractOverrides(a, this); const bRecord = extractOverrides(b, this); - if (aRecord.formatter) { - assert(bRecord.formatter == aRecord.formatter, 'formatters for same Temporal type should be identical'); - formatter = aRecord.formatter; - formatArgs = [ES.epochNsToMs(aRecord.epochNs, 'floor'), ES.epochNsToMs(bRecord.epochNs, 'floor')]; - } + assert(bRecord.formatter == aRecord.formatter, 'formatters for same Temporal type should be identical'); + formatter = aRecord.formatter; + formatArgs = [ES.epochNsToMs(aRecord.epochNs, 'floor'), ES.epochNsToMs(bRecord.epochNs, 'floor')]; aDayAdjust = aRecord.dayAdjust ?? 0; bDayAdjust = bRecord.dayAdjust ?? 0; } else { @@ -482,17 +480,15 @@ function formatRangeToParts(a, b) { let formatter; let aDayAdjust = 0; let bDayAdjust = 0; - if (isTemporalObject(a) || isTemporalObject(b)) { + if (isFormattableTemporalObject(a) || isFormattableTemporalObject(b)) { if (!sameTemporalType(a, b)) { throw new TypeErrorCtor('Intl.DateTimeFormat.formatRangeToParts accepts two values of the same type'); } const aRecord = extractOverrides(a, this); const bRecord = extractOverrides(b, this); - if (aRecord.formatter) { - assert(bRecord.formatter == aRecord.formatter, 'formatters for same Temporal type should be identical'); - formatter = aRecord.formatter; - formatArgs = [ES.epochNsToMs(aRecord.epochNs, 'floor'), ES.epochNsToMs(bRecord.epochNs, 'floor')]; - } + assert(bRecord.formatter == aRecord.formatter, 'formatters for same Temporal type should be identical'); + formatter = aRecord.formatter; + formatArgs = [ES.epochNsToMs(aRecord.epochNs, 'floor'), ES.epochNsToMs(bRecord.epochNs, 'floor')]; aDayAdjust = aRecord.dayAdjust ?? 0; bDayAdjust = bRecord.dayAdjust ?? 0; } else { @@ -740,7 +736,7 @@ function hasAnyDateTimeOptions(originalOptions) { return hasDateOptions(originalOptions) || hasTimeOptions(originalOptions); } -function isTemporalObject(obj) { +function isFormattableTemporalObject(obj) { return ( ES.IsTemporalDate(obj) || ES.IsTemporalTime(obj) || @@ -753,12 +749,12 @@ function isTemporalObject(obj) { } function toDateTimeFormattable(value) { - if (isTemporalObject(value)) return value; + if (isFormattableTemporalObject(value)) return value; return ES.ToNumber(value); } function sameTemporalType(x, y) { - if (!isTemporalObject(x) || !isTemporalObject(y)) return false; + if (!isFormattableTemporalObject(x) || !isFormattableTemporalObject(y)) return false; if (ES.IsTemporalTime(x) && !ES.IsTemporalTime(y)) return false; if (ES.IsTemporalDate(x) && !ES.IsTemporalDate(y)) return false; if (ES.IsTemporalDateTime(x) && !ES.IsTemporalDateTime(y)) return false; diff --git a/polyfill/test262 b/polyfill/test262 index ac3a4ba5b..d0c1b4555 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit ac3a4ba5ba9e67be0472a3853e5db9810e43f8cf +Subproject commit d0c1b4555b03dd404873fd6422a4b5da00136500