From 1a0188334811f362f4ca1c9a947b3616ff3165ba Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 17 May 2021 17:33:23 -0700 Subject: [PATCH] Use singular unit names in polyfill This is the implementation of the normative changes in #1509 in the polyfill, accompanied by test262 tests to ensure that passing plural and singular values for largestUnit, smallestUnit, and unit behaves the same; and also that Calendar.dateUntil() is only ever called with options bags that contain singular values for largestUnit. See: #1491 --- polyfill/lib/calendar.mjs | 26 +- polyfill/lib/duration.mjs | 29 +- polyfill/lib/ecmascript.mjs | 380 ++++++------------ polyfill/lib/instant.mjs | 47 +-- polyfill/lib/plaindate.mjs | 30 +- polyfill/lib/plaindatetime.mjs | 15 +- polyfill/lib/plaintime.mjs | 51 +-- polyfill/lib/plainyearmonth.mjs | 38 +- polyfill/lib/zoneddatetime.mjs | 19 +- .../dateUntil/largestunit-plurals-accepted.js | 19 + ...euntil-called-with-singular-largestunit.js | 77 ++++ ...euntil-called-with-singular-largestunit.js | 159 ++++++++ .../round/largestunit-plurals-accepted.js | 24 ++ .../round/smallestunit-plurals-accepted.js | 24 ++ ...euntil-called-with-singular-largestunit.js | 77 ++++ .../toString/smallestunit-plurals-accepted.js | 17 + ...euntil-called-with-singular-largestunit.js | 92 +++++ .../prototype/total/unit-plurals-accepted.js | 24 ++ .../round/smallestunit-plurals-accepted.js | 19 + .../since/largestunit-plurals-accepted.js | 20 + .../since/smallestunit-plurals-accepted.js | 20 + .../toString/smallestunit-plurals-accepted.js | 18 + .../until/largestunit-plurals-accepted.js | 20 + .../until/smallestunit-plurals-accepted.js | 20 + ...euntil-called-with-singular-largestunit.js | 26 ++ .../since/largestunit-plurals-accepted.js | 18 + .../since/smallestunit-plurals-accepted.js | 18 + ...euntil-called-with-singular-largestunit.js | 26 ++ .../until/largestunit-plurals-accepted.js | 18 + .../until/smallestunit-plurals-accepted.js | 18 + .../round/smallestunit-plurals-accepted.js | 20 + ...euntil-called-with-singular-largestunit.js | 35 ++ .../since/largestunit-plurals-accepted.js | 24 ++ .../since/smallestunit-plurals-accepted.js | 24 ++ .../toString/smallestunit-plurals-accepted.js | 18 + ...euntil-called-with-singular-largestunit.js | 35 ++ .../until/largestunit-plurals-accepted.js | 24 ++ .../until/smallestunit-plurals-accepted.js | 24 ++ .../round/smallestunit-plurals-accepted.js | 19 + .../since/largestunit-plurals-accepted.js | 20 + .../since/smallestunit-plurals-accepted.js | 20 + .../toString/smallestunit-plurals-accepted.js | 18 + .../until/largestunit-plurals-accepted.js | 20 + .../until/smallestunit-plurals-accepted.js | 20 + ...euntil-called-with-singular-largestunit.js | 24 ++ .../since/largestunit-plurals-accepted.js | 16 + .../since/smallestunit-plurals-accepted.js | 16 + ...euntil-called-with-singular-largestunit.js | 24 ++ .../until/largestunit-plurals-accepted.js | 16 + .../until/smallestunit-plurals-accepted.js | 16 + .../round/smallestunit-plurals-accepted.js | 20 + ...euntil-called-with-singular-largestunit.js | 113 ++++++ .../since/largestunit-plurals-accepted.js | 24 ++ .../since/smallestunit-plurals-accepted.js | 24 ++ .../toString/smallestunit-plurals-accepted.js | 18 + ...euntil-called-with-singular-largestunit.js | 113 ++++++ .../until/largestunit-plurals-accepted.js | 24 ++ .../until/smallestunit-plurals-accepted.js | 24 ++ polyfill/test/helpers/temporalHelpers.js | 92 +++++ 59 files changed, 1857 insertions(+), 417 deletions(-) create mode 100644 polyfill/test/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js create mode 100644 polyfill/test/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/Duration/prototype/round/largestunit-plurals-accepted.js create mode 100644 polyfill/test/Duration/prototype/round/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/Duration/prototype/toString/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/Duration/prototype/total/unit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/round/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/toString/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/Instant/prototype/until/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainDate/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDate/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainDate/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDate/prototype/until/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainDateTime/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainDateTime/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/round/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/toString/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainTime/prototype/until/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js create mode 100644 polyfill/test/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js create mode 100644 polyfill/test/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index 0310ea8bab..8659de5cfe 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -79,14 +79,12 @@ export class Calendar { one = ES.ToTemporalDate(one); two = ES.ToTemporalDate(two); options = ES.GetOptionsObject(options); - const largestUnit = ES.ToLargestTemporalUnit(options, 'days', [ - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]); + const largestUnit = ES.ToLargestTemporalUnit( + options, + 'auto', + ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'], + 'day' + ); const { years, months, weeks, days } = impl[GetSlot(this, CALENDAR_ID)].dateUntil(one, two, largestUnit); const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration(years, months, weeks, days, 0, 0, 0, 0, 0, 0); @@ -793,22 +791,22 @@ const nonIsoHelperBase = { let months = 0; let years = 0; switch (largestUnit) { - case 'days': + case 'day': days = this.calendarDaysUntil(calendarOne, calendarTwo, cache); break; - case 'weeks': { + case 'week': { const totalDays = this.calendarDaysUntil(calendarOne, calendarTwo, cache); days = totalDays % 7; weeks = (totalDays - days) / 7; break; } - case 'months': - case 'years': { + case 'month': + case 'year': { const diffYears = calendarTwo.year - calendarOne.year; const diffMonths = calendarTwo.month - calendarOne.month; const diffDays = calendarTwo.day - calendarOne.day; const sign = this.compareCalendarDates(calendarTwo, calendarOne); - if (largestUnit === 'years' && diffYears) { + if (largestUnit === 'year' && diffYears) { const isOneFurtherInYear = diffMonths * sign < 0 || (diffMonths === 0 && diffDays * sign < 0); years = isOneFurtherInYear ? diffYears - sign : diffYears; } @@ -898,7 +896,7 @@ const nonIsoHelperBase = { twoIso.year, twoIso.month, twoIso.day, - 'days' + 'day' ); return duration.days; }, diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index bdec563ebc..3736947046 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -323,14 +323,14 @@ export class Duration { nanoseconds ); options = ES.GetOptionsObject(options); - let smallestUnit = ES.ToSmallestTemporalDurationUnit(options, undefined); + let smallestUnit = ES.ToSmallestTemporalUnit(options, undefined); let smallestUnitPresent = true; if (!smallestUnit) { smallestUnitPresent = false; - smallestUnit = 'nanoseconds'; + smallestUnit = 'nanosecond'; } - defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits(defaultLargestUnit, smallestUnit); - let largestUnit = ES.ToLargestTemporalDurationUnit(options); + defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit); + let largestUnit = ES.ToLargestTemporalUnit(options, undefined); let largestUnitPresent = true; if (!largestUnit) { largestUnitPresent = false; @@ -342,19 +342,7 @@ export class Duration { } ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); - const maximumIncrements = { - years: undefined, - months: undefined, - weeks: undefined, - days: undefined, - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit); let relativeTo = ES.ToRelativeTemporalObject(options); ({ years, months, weeks, days } = ES.UnbalanceDurationRelative( @@ -495,7 +483,8 @@ export class Duration { toString(options = undefined) { if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver'); options = ES.GetOptionsObject(options); - const { precision, unit, increment } = ES.ToDurationSecondsStringPrecision(options); + const { precision, unit, increment } = ES.ToSecondsStringPrecision(options); + if (precision === 'minute') throw new RangeError('smallestUnit must not be "minute"'); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); return ES.TemporalDurationToString(this, precision, { unit, increment, roundingMode }); } @@ -559,8 +548,8 @@ export class Duration { const shift1 = ES.CalculateOffsetShift(relativeTo, y1, mon1, w1, d1, h1, min1, s1, ms1, µs1, ns1); const shift2 = ES.CalculateOffsetShift(relativeTo, y2, mon2, w2, d2, h2, min2, s2, ms2, µs2, ns2); if (y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0) { - ({ days: d1 } = ES.UnbalanceDurationRelative(y1, mon1, w1, d1, 'days', relativeTo)); - ({ days: d2 } = ES.UnbalanceDurationRelative(y2, mon2, w2, d2, 'days', relativeTo)); + ({ days: d1 } = ES.UnbalanceDurationRelative(y1, mon1, w1, d1, 'day', relativeTo)); + ({ days: d2 } = ES.UnbalanceDurationRelative(y2, mon2, w2, d2, 'day', relativeTo)); } ns1 = ES.TotalDurationNanoseconds(d1, h1, min1, s1, ms1, µs1, ns1, shift1); ns2 = ES.TotalDurationNanoseconds(d2, h2, min2, s2, ms2, µs2, ns2, shift2); diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 7c1903bfb8..720afd967a 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -110,6 +110,31 @@ const BUILTIN_CASTS = new Map([ ['offset', ToString] ]); +const ALLOWED_UNITS = [ + 'year', + 'month', + 'week', + 'day', + 'hour', + 'minute', + 'second', + 'millisecond', + 'microsecond', + 'nanosecond' +]; +const SINGULAR_PLURAL_UNITS = [ + ['years', 'year'], + ['months', 'month'], + ['weeks', 'week'], + ['days', 'day'], + ['hours', 'hour'], + ['minutes', 'minute'], + ['seconds', 'second'], + ['milliseconds', 'millisecond'], + ['microseconds', 'microsecond'], + ['nanoseconds', 'nanosecond'] +]; + import * as PARSE from './regex.mjs'; const ES2020 = { @@ -626,30 +651,21 @@ export const ES = ObjectAssign({}, ES2020, { }, ToTemporalDateTimeRoundingIncrement: (options, smallestUnit) => { const maximumIncrements = { - years: undefined, - months: undefined, - weeks: undefined, - days: undefined, - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 + year: undefined, + month: undefined, + week: undefined, + day: undefined, + hour: 24, + minute: 60, + second: 60, + millisecond: 1000, + microsecond: 1000, + nanosecond: 1000 }; return ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); }, ToSecondsStringPrecision: (options) => { - const singular = new Map([ - ['minutes', 'minute'], - ['seconds', 'second'], - ['milliseconds', 'millisecond'], - ['microseconds', 'microsecond'], - ['nanoseconds', 'nanosecond'] - ]); - const allowed = new Set(['minute', 'second', 'millisecond', 'microsecond', 'nanosecond']); - let smallestUnit = ES.GetOption(options, 'smallestUnit', [...allowed, ...singular.keys()], undefined); - if (singular.has(smallestUnit)) smallestUnit = singular.get(smallestUnit); + let smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week', 'day', 'hour']); switch (smallestUnit) { case 'minute': return { precision: 'minute', unit: 'minute', increment: 1 }; @@ -687,174 +703,35 @@ export const ES = ObjectAssign({}, ES2020, { return { precision, unit: 'nanosecond', increment: 10 ** (9 - precision) }; } }, - ToDurationSecondsStringPrecision: (options) => { - const plural = new Map([ - ['second', 'seconds'], - ['millisecond', 'milliseconds'], - ['microsecond', 'microseconds'], - ['nanosecond', 'nanoseconds'] - ]); - const allowed = new Set(['seconds', 'milliseconds', 'microseconds', 'nanoseconds']); - let smallestUnit = ES.GetOption(options, 'smallestUnit', [...allowed, ...plural.keys()], undefined); - if (plural.has(smallestUnit)) smallestUnit = plural.get(smallestUnit); - switch (smallestUnit) { - case 'seconds': - return { precision: 0, unit: 'seconds', increment: 1 }; - case 'milliseconds': - return { precision: 3, unit: 'milliseconds', increment: 1 }; - case 'microseconds': - return { precision: 6, unit: 'microseconds', increment: 1 }; - case 'nanoseconds': - return { precision: 9, unit: 'nanoseconds', increment: 1 }; - default: // fall through if option not given - } - let digits = options.fractionalSecondDigits; - if (digits === undefined || digits === 'auto') return { precision: 'auto', unit: 'nanoseconds', increment: 1 }; - digits = ES.ToNumber(digits); - if (NumberIsNaN(digits) || digits < 0 || digits > 9) { - throw new RangeError(`fractionalSecondDigits must be 'auto' or 0 through 9, not ${digits}`); - } - const precision = MathFloor(digits); - switch (precision) { - case 0: - return { precision, unit: 'seconds', increment: 1 }; - case 1: - case 2: - case 3: - return { precision, unit: 'milliseconds', increment: 10 ** (3 - precision) }; - case 4: - case 5: - case 6: - return { precision, unit: 'microseconds', increment: 10 ** (6 - precision) }; - case 7: - case 8: - case 9: - return { precision, unit: 'nanoseconds', increment: 10 ** (9 - precision) }; - } - }, - ToLargestTemporalUnit: (options, fallback, disallowedStrings = []) => { - const plural = new Map( - [ - ['year', 'years'], - ['month', 'months'], - ['day', 'days'], - ['hour', 'hours'], - ['minute', 'minutes'], - ['second', 'seconds'], - ['millisecond', 'milliseconds'], - ['microsecond', 'microseconds'], - ['nanosecond', 'nanoseconds'] - ].filter(([, pl]) => !disallowedStrings.includes(pl)) - ); - const allowed = new Set([ - 'years', - 'months', - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]); + ToLargestTemporalUnit: (options, fallback, disallowedStrings = [], autoValue) => { + const singular = new Map(SINGULAR_PLURAL_UNITS.filter(([, sing]) => !disallowedStrings.includes(sing))); + const allowed = new Set(ALLOWED_UNITS); for (const s of disallowedStrings) { allowed.delete(s); } - const retval = ES.GetOption(options, 'largestUnit', ['auto', ...allowed, ...plural.keys()], 'auto'); - if (retval === 'auto') return fallback; - if (plural.has(retval)) return plural.get(retval); - return retval; - }, - ToLargestTemporalDurationUnit: (options) => { - const plural = new Map([ - ['year', 'years'], - ['month', 'months'], - ['day', 'days'], - ['hour', 'hours'], - ['minute', 'minutes'], - ['second', 'seconds'], - ['millisecond', 'milliseconds'], - ['microsecond', 'microseconds'], - ['nanosecond', 'nanoseconds'] - ]); - const retval = ES.GetOption(options, 'largestUnit', ['auto', ...plural.keys(), ...plural.values(), 'weeks']); - if (plural.has(retval)) return plural.get(retval); + const retval = ES.GetOption(options, 'largestUnit', ['auto', ...allowed, ...singular.keys()], fallback); + if (retval === 'auto' && autoValue !== undefined) return autoValue; + if (singular.has(retval)) return singular.get(retval); return retval; }, - ToSmallestTemporalUnit: (options, disallowedStrings = []) => { - const singular = new Map( - [ - ['days', 'day'], - ['hours', 'hour'], - ['minutes', 'minute'], - ['seconds', 'second'], - ['milliseconds', 'millisecond'], - ['microseconds', 'microsecond'], - ['nanoseconds', 'nanosecond'] - ].filter(([, sing]) => !disallowedStrings.includes(sing)) - ); - const allowed = new Set(['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']); + ToSmallestTemporalUnit: (options, fallback, disallowedStrings = []) => { + const singular = new Map(SINGULAR_PLURAL_UNITS.filter(([, sing]) => !disallowedStrings.includes(sing))); + const allowed = new Set(ALLOWED_UNITS); for (const s of disallowedStrings) { allowed.delete(s); } - const value = ES.GetOption(options, 'smallestUnit', [...allowed, ...singular.keys()], undefined); - if (value === undefined) throw new RangeError('smallestUnit option is required'); + const value = ES.GetOption(options, 'smallestUnit', [...allowed, ...singular.keys()], fallback); if (singular.has(value)) return singular.get(value); return value; }, - ToSmallestTemporalDurationUnit: (options, fallback, disallowedStrings = []) => { - const plural = new Map( - [ - ['year', 'years'], - ['month', 'months'], - ['day', 'days'], - ['hour', 'hours'], - ['minute', 'minutes'], - ['second', 'seconds'], - ['millisecond', 'milliseconds'], - ['microsecond', 'microseconds'], - ['nanosecond', 'nanoseconds'] - ].filter(([, pl]) => !disallowedStrings.includes(pl)) - ); - const allowed = new Set([ - 'years', - 'months', - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]); - for (const s of disallowedStrings) { - allowed.delete(s); - } - const value = ES.GetOption(options, 'smallestUnit', [...allowed, ...plural.keys()], fallback); - if (plural.has(value)) return plural.get(value); - return value; - }, ToTemporalDurationTotalUnit: (options) => { - // This AO is identical to ToSmallestTemporalDurationUnit, except: + // This AO is identical to ToSmallestTemporalUnit, except: // - default is always `undefined` (caller will throw if omitted) // - option is named `unit` (not `smallestUnit`) // - all units are valid (no `disallowedStrings`) - const plural = new Map([ - ['year', 'years'], - ['month', 'months'], - ['day', 'days'], - ['hour', 'hours'], - ['minute', 'minutes'], - ['second', 'seconds'], - ['millisecond', 'milliseconds'], - ['microsecond', 'microseconds'], - ['nanosecond', 'nanoseconds'] - ]); - // "week" doesn't exist in Temporal as a non-plural unit, so don't allow it - const value = ES.GetOption(options, 'unit', [...plural.values(), ...plural.keys(), 'weeks'], undefined); - if (plural.has(value)) return plural.get(value); + const singular = new Map(SINGULAR_PLURAL_UNITS); + const value = ES.GetOption(options, 'unit', [...singular.values(), ...singular.keys()], undefined); + if (singular.has(value)) return singular.get(value); return value; }, ToRelativeTemporalObject: (options) => { @@ -951,19 +828,7 @@ export const ES = ObjectAssign({}, ES2020, { ); }, ValidateTemporalUnitRange: (largestUnit, smallestUnit) => { - const validUnits = [ - 'years', - 'months', - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]; - if (validUnits.indexOf(largestUnit) > validUnits.indexOf(smallestUnit)) { + if (ALLOWED_UNITS.indexOf(largestUnit) > ALLOWED_UNITS.indexOf(smallestUnit)) { throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`); } }, @@ -979,6 +844,7 @@ export const ES = ObjectAssign({}, ES2020, { microseconds, nanoseconds ) => { + const singular = new Map(SINGULAR_PLURAL_UNITS); for (const [prop, v] of ObjectEntries({ years, months, @@ -991,24 +857,12 @@ export const ES = ObjectAssign({}, ES2020, { microseconds, nanoseconds })) { - if (v !== 0) return prop; - } - return 'nanoseconds'; - }, - LargerOfTwoTemporalDurationUnits: (unit1, unit2) => { - const validUnits = [ - 'years', - 'months', - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]; - if (validUnits.indexOf(unit1) > validUnits.indexOf(unit2)) return unit2; + if (v !== 0) return singular.get(prop); + } + return 'nanosecond'; + }, + LargerOfTwoTemporalUnits: (unit1, unit2) => { + if (ALLOWED_UNITS.indexOf(unit1) > ALLOWED_UNITS.indexOf(unit2)) return unit2; return unit1; }, CastIfDefined: (value, cast) => { @@ -2713,7 +2567,7 @@ export const ES = ObjectAssign({}, ES2020, { GetSlot(dtEnd, ISO_MICROSECOND), GetSlot(dtEnd, ISO_NANOSECOND), calendar, - 'days' + 'day' ); let intermediateNs = ES.AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days, 0, 0, 0, 0, 0, 0); // may disambiguate @@ -2792,7 +2646,7 @@ export const ES = ObjectAssign({}, ES2020, { 0 ); } - if (largestUnit === 'years' || largestUnit === 'months' || largestUnit === 'weeks' || largestUnit === 'days') { + if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || largestUnit === 'day') { ({ days, nanoseconds } = ES.NanosecondsToDays(nanoseconds, relativeTo)); } else { days = 0; @@ -2803,36 +2657,36 @@ export const ES = ObjectAssign({}, ES2020, { microseconds = milliseconds = seconds = minutes = hours = bigInt.zero; switch (largestUnit) { - case 'years': - case 'months': - case 'weeks': - case 'days': - case 'hours': + case 'year': + case 'month': + case 'week': + case 'day': + case 'hour': ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); ({ quotient: minutes, remainder: seconds } = seconds.divmod(60)); ({ quotient: hours, remainder: minutes } = minutes.divmod(60)); break; - case 'minutes': + case 'minute': ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); ({ quotient: minutes, remainder: seconds } = seconds.divmod(60)); break; - case 'seconds': + case 'second': ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); break; - case 'milliseconds': + case 'millisecond': ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); break; - case 'microseconds': + case 'microsecond': ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); break; - case 'nanoseconds': + case 'nanosecond': break; default: throw new Error('assert not reached'); @@ -2862,10 +2716,10 @@ export const ES = ObjectAssign({}, ES2020, { const oneWeek = new TemporalDuration(0, 0, sign); switch (largestUnit) { - case 'years': + case 'year': // no-op break; - case 'months': + case 'month': { if (!calendar) throw new RangeError('a starting point is required for months balancing'); // balance years down to months @@ -2875,7 +2729,7 @@ export const ES = ObjectAssign({}, ES2020, { const addOptions = ObjectCreate(null); const newRelativeTo = ES.CalendarDateAdd(calendar, relativeTo, oneYear, addOptions, dateAdd); const untilOptions = ObjectCreate(null); - untilOptions.largestUnit = 'months'; + untilOptions.largestUnit = 'month'; const oneYearMonths = ES.CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil) .months; relativeTo = newRelativeTo; @@ -2884,7 +2738,7 @@ export const ES = ObjectAssign({}, ES2020, { } } break; - case 'weeks': + case 'week': if (!calendar) throw new RangeError('a starting point is required for weeks balancing'); // balance years down to days while (MathAbs(years) > 0) { @@ -2950,7 +2804,7 @@ export const ES = ObjectAssign({}, ES2020, { const oneWeek = new TemporalDuration(0, 0, sign); switch (largestUnit) { - case 'years': { + case 'year': { if (!calendar) throw new RangeError('a starting point is required for years balancing'); // balance days up to years let newRelativeTo, oneYearDays; @@ -2978,7 +2832,7 @@ export const ES = ObjectAssign({}, ES2020, { newRelativeTo = ES.CalendarDateAdd(calendar, relativeTo, oneYear, addOptions, dateAdd); const dateUntil = ES.GetMethod(calendar, 'dateUntil'); const untilOptions = ObjectCreate(null); - untilOptions.largestUnit = 'months'; + untilOptions.largestUnit = 'month'; let oneYearMonths = ES.CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).months; while (MathAbs(months) >= MathAbs(oneYearMonths)) { months -= oneYearMonths; @@ -2987,12 +2841,12 @@ export const ES = ObjectAssign({}, ES2020, { const addOptions = ObjectCreate(null); newRelativeTo = ES.CalendarDateAdd(calendar, relativeTo, oneYear, addOptions, dateAdd); const untilOptions = ObjectCreate(null); - untilOptions.largestUnit = 'months'; + untilOptions.largestUnit = 'month'; oneYearMonths = ES.CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).months; } break; } - case 'months': { + case 'month': { if (!calendar) throw new RangeError('a starting point is required for months balancing'); // balance days up to months let newRelativeTo, oneMonthDays; @@ -3005,7 +2859,7 @@ export const ES = ObjectAssign({}, ES2020, { } break; } - case 'weeks': { + case 'week': { if (!calendar) throw new RangeError('a starting point is required for weeks balancing'); // balance days up to weeks let newRelativeTo, oneWeekDays; @@ -3139,8 +2993,8 @@ export const ES = ObjectAssign({}, ES2020, { DifferenceISODate: (y1, m1, d1, y2, m2, d2, largestUnit = 'days') => { switch (largestUnit) { - case 'years': - case 'months': { + case 'year': + case 'month': { const sign = -ES.CompareISODate(y1, m1, d1, y2, m2, d2); if (sign === 0) return { years: 0, months: 0, weeks: 0, days: 0 }; @@ -3151,7 +3005,7 @@ export const ES = ObjectAssign({}, ES2020, { let mid = ES.AddISODate(y1, m1, d1, years, 0, 0, 0, 'constrain'); let midSign = -ES.CompareISODate(mid.year, mid.month, mid.day, y2, m2, d2); if (midSign === 0) { - return largestUnit === 'years' + return largestUnit === 'year' ? { years, months: 0, weeks: 0, days: 0 } : { years: 0, months: years * 12, weeks: 0, days: 0 }; } @@ -3163,7 +3017,7 @@ export const ES = ObjectAssign({}, ES2020, { mid = ES.AddISODate(y1, m1, d1, years, months, 0, 0, 'constrain'); midSign = -ES.CompareISODate(mid.year, mid.month, mid.day, y2, m2, d2); if (midSign === 0) { - return largestUnit === 'years' + return largestUnit === 'year' ? { years, months, weeks: 0, days: 0 } : { years: 0, months: months + years * 12, weeks: 0, days: 0 }; } @@ -3199,14 +3053,14 @@ export const ES = ObjectAssign({}, ES2020, { days = end.day + (ES.ISODaysInMonth(mid.year, mid.month) - mid.day); } - if (largestUnit === 'months') { + if (largestUnit === 'month') { months += years * 12; years = 0; } return { years, months, weeks: 0, days }; } - case 'weeks': - case 'days': { + case 'week': + case 'day': { let larger, smaller, sign; if (ES.CompareISODate(y1, m1, d1, y2, m2, d2) < 0) { smaller = { year: y1, month: m1, day: d1 }; @@ -3225,7 +3079,7 @@ export const ES = ObjectAssign({}, ES2020, { years -= 1; } let weeks = 0; - if (largestUnit === 'weeks') { + if (largestUnit === 'week') { weeks = MathFloor(days / 7); days %= 7; } @@ -3356,7 +3210,7 @@ export const ES = ObjectAssign({}, ES2020, { const date1 = ES.CreateTemporalDate(y1, mon1, d1, calendar); const date2 = ES.CreateTemporalDate(y2, mon2, d2, calendar); - const dateLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', largestUnit); + const dateLargestUnit = ES.LargerOfTwoTemporalUnits('day', largestUnit); const untilOptions = { ...options, largestUnit: dateLargestUnit }; let { years, months, weeks, days } = ES.CalendarDateUntil(calendar, date1, date2, untilOptions); // Signs of date part and time part may not agree; balance them together @@ -3433,7 +3287,7 @@ export const ES = ObjectAssign({}, ES2020, { 0, 0, timeRemainderNs, - 'hours' + 'hour' ); return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; }, @@ -3513,11 +3367,11 @@ export const ES = ObjectAssign({}, ES2020, { ) => { const largestUnit1 = ES.DefaultTemporalLargestUnit(y1, mon1, w1, d1, h1, min1, s1, ms1, µs1, ns1); const largestUnit2 = ES.DefaultTemporalLargestUnit(y2, mon2, w2, d2, h2, min2, s2, ms2, µs2, ns2); - const largestUnit = ES.LargerOfTwoTemporalDurationUnits(largestUnit1, largestUnit2); + const largestUnit = ES.LargerOfTwoTemporalUnits(largestUnit1, largestUnit2); let years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds; if (!relativeTo) { - if (largestUnit === 'years' || largestUnit === 'months' || largestUnit === 'weeks') { + if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week') { throw new RangeError('relativeTo is required for years, months, or weeks arithmetic'); } years = months = weeks = 0; @@ -3549,7 +3403,7 @@ export const ES = ObjectAssign({}, ES2020, { const secondAddOptions = ObjectCreate(null); const end = ES.CalendarDateAdd(calendar, intermediate, dateDuration2, secondAddOptions, dateAdd); - const dateLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', largestUnit); + const dateLargestUnit = ES.LargerOfTwoTemporalUnits('day', largestUnit); const differenceOptions = ObjectCreate(null); differenceOptions.largestUnit = dateLargestUnit; ({ years, months, weeks, days } = ES.CalendarDateUntil(calendar, datePart, end, differenceOptions)); @@ -3599,7 +3453,7 @@ export const ES = ObjectAssign({}, ES2020, { µs2, ns2 ); - if (largestUnit !== 'years' && largestUnit !== 'months' && largestUnit !== 'weeks' && largestUnit !== 'days') { + if (largestUnit !== 'year' && largestUnit !== 'month' && largestUnit !== 'week' && largestUnit !== 'day') { // The user is only asking for a time difference, so return difference of instants. years = 0; months = 0; @@ -3609,7 +3463,7 @@ export const ES = ObjectAssign({}, ES2020, { GetSlot(relativeTo, EPOCHNANOSECONDS), endNs, 1, - 'nanoseconds', + 'nanosecond', 'halfExpand' )); ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceDuration( @@ -3878,7 +3732,7 @@ export const ES = ObjectAssign({}, ES2020, { GetSlot(later, ISO_YEAR), GetSlot(later, ISO_MONTH), GetSlot(later, ISO_DAY), - 'days' + 'day' ).days; }, MoveRelativeDate: (calendar, relativeTo, duration) => { @@ -3937,11 +3791,11 @@ export const ES = ObjectAssign({}, ES2020, { ) => { if ( !ES.IsTemporalZonedDateTime(relativeTo) || - unit === 'years' || - unit === 'months' || - unit === 'weeks' || - unit === 'days' || - (unit === 'nanoseconds' && increment === 1) + unit === 'year' || + unit === 'month' || + unit === 'week' || + unit === 'day' || + (unit === 'nanosecond' && increment === 1) ) { return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; } @@ -4034,7 +3888,7 @@ export const ES = ObjectAssign({}, ES2020, { 0, 0, timeRemainderNs.toJSNumber(), - 'hours' + 'hour' )); } return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; @@ -4074,7 +3928,7 @@ export const ES = ObjectAssign({}, ES2020, { // First convert time units up to days, if rounding to days or higher units. // If rounding relative to a ZonedDateTime, then some days may not be 24h. let dayLengthNs; - if (unit === 'years' || unit === 'months' || unit === 'weeks' || unit === 'days') { + if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') { nanoseconds = ES.TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); let intermediate; if (zdtRelative) { @@ -4089,7 +3943,7 @@ export const ES = ObjectAssign({}, ES2020, { let total; switch (unit) { - case 'years': { + case 'year': { if (!calendar) throw new RangeError('A starting point is required for years rounding'); // convert months and weeks to days by calculating difference( @@ -4114,7 +3968,7 @@ export const ES = ObjectAssign({}, ES2020, { const thirdAddOptions = ObjectCreate(null); const daysLater = ES.CalendarDateAdd(calendar, relativeTo, { days }, thirdAddOptions, dateAdd); const untilOptions = ObjectCreate(null); - untilOptions.largestUnit = 'years'; + untilOptions.largestUnit = 'year'; const yearsPassed = ES.CalendarDateUntil(calendar, relativeTo, daysLater, untilOptions).years; years += yearsPassed; const oldRelativeTo = relativeTo; @@ -4140,7 +3994,7 @@ export const ES = ObjectAssign({}, ES2020, { nanoseconds = months = weeks = days = 0; break; } - case 'months': { + case 'month': { if (!calendar) throw new RangeError('A starting point is required for months rounding'); // convert weeks to days by calculating difference(relativeTo + @@ -4182,7 +4036,7 @@ export const ES = ObjectAssign({}, ES2020, { nanoseconds = weeks = days = 0; break; } - case 'weeks': { + case 'week': { if (!calendar) throw new RangeError('A starting point is required for weeks rounding'); // Weeks may be different lengths of days depending on the calendar, // convert days to weeks in a loop as described above under 'years'. @@ -4204,7 +4058,7 @@ export const ES = ObjectAssign({}, ES2020, { nanoseconds = days = 0; break; } - case 'days': { + case 'day': { const divisor = bigInt(dayLengthNs); nanoseconds = divisor.multiply(days).plus(nanoseconds); const rounded = ES.RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); @@ -4213,7 +4067,7 @@ export const ES = ObjectAssign({}, ES2020, { nanoseconds = 0; break; } - case 'hours': { + case 'hour': { const divisor = 3600e9; nanoseconds = bigInt(hours) .multiply(3600e9) @@ -4228,7 +4082,7 @@ export const ES = ObjectAssign({}, ES2020, { minutes = seconds = milliseconds = microseconds = nanoseconds = 0; break; } - case 'minutes': { + case 'minute': { const divisor = 60e9; nanoseconds = bigInt(minutes) .multiply(60e9) @@ -4242,7 +4096,7 @@ export const ES = ObjectAssign({}, ES2020, { seconds = milliseconds = microseconds = nanoseconds = 0; break; } - case 'seconds': { + case 'second': { const divisor = 1e9; nanoseconds = bigInt(seconds) .multiply(1e9) @@ -4255,7 +4109,7 @@ export const ES = ObjectAssign({}, ES2020, { milliseconds = microseconds = nanoseconds = 0; break; } - case 'milliseconds': { + case 'millisecond': { const divisor = 1e6; nanoseconds = bigInt(milliseconds).multiply(1e6).plus(bigInt(microseconds).multiply(1e3)).plus(nanoseconds); total = nanoseconds.toJSNumber() / divisor; @@ -4264,7 +4118,7 @@ export const ES = ObjectAssign({}, ES2020, { microseconds = nanoseconds = 0; break; } - case 'microseconds': { + case 'microsecond': { const divisor = 1e3; nanoseconds = bigInt(microseconds).multiply(1e3).plus(nanoseconds); total = nanoseconds.toJSNumber() / divisor; @@ -4273,7 +4127,7 @@ export const ES = ObjectAssign({}, ES2020, { nanoseconds = 0; break; } - case 'nanoseconds': { + case 'nanosecond': { total = nanoseconds; nanoseconds = ES.RoundNumberToIncrement(bigInt(nanoseconds), increment, roundingMode); break; @@ -4406,15 +4260,9 @@ function bisect(getState, left, right, lstate = getState(left), rstate = getStat const nsPerTimeUnit = { hour: 3600e9, - hours: 3600e9, minute: 60e9, - minutes: 60e9, second: 1e9, - seconds: 1e9, millisecond: 1e6, - milliseconds: 1e6, microsecond: 1e3, - microseconds: 1e3, - nanosecond: 1, - nanoseconds: 1 + nanosecond: 1 }; diff --git a/polyfill/lib/instant.mjs b/polyfill/lib/instant.mjs index faf59287c5..c112c7f5f8 100644 --- a/polyfill/lib/instant.mjs +++ b/polyfill/lib/instant.mjs @@ -7,6 +7,16 @@ import { EPOCHNANOSECONDS, CreateSlots, GetSlot, SetSlot } from './slots.mjs'; import bigInt from 'big-integer'; +const DISALLOWED_UNITS = ['year', 'month', 'week', 'day']; +const MAX_DIFFERENCE_INCREMENTS = { + hour: 24, + minute: 60, + second: 60, + millisecond: 1000, + microsecond: 1000, + nanosecond: 1000 +}; + export class Instant { constructor(epochNanoseconds) { // Note: if the argument is not passed, ToBigInt(undefined) will throw. This check exists only @@ -96,22 +106,13 @@ export class Instant { until(other, options = undefined) { if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver'); other = ES.ToTemporalInstant(other); - const disallowedUnits = ['years', 'months', 'weeks', 'days']; options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds', disallowedUnits); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('seconds', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit, disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('second', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); - const maximumIncrements = { - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_DIFFERENCE_INCREMENTS[smallestUnit], false); const onens = GetSlot(this, EPOCHNANOSECONDS); const twons = GetSlot(other, EPOCHNANOSECONDS); let { seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceInstant( @@ -138,22 +139,13 @@ export class Instant { since(other, options = undefined) { if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver'); other = ES.ToTemporalInstant(other); - const disallowedUnits = ['years', 'months', 'weeks', 'days']; options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds', disallowedUnits); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('seconds', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit, disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('second', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); - const maximumIncrements = { - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_DIFFERENCE_INCREMENTS[smallestUnit], false); const onens = GetSlot(other, EPOCHNANOSECONDS); const twons = GetSlot(this, EPOCHNANOSECONDS); let { seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceInstant( @@ -181,7 +173,8 @@ export class Instant { if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver'); if (options === undefined) throw new TypeError('options parameter is required'); options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, ['day']); + const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, DISALLOWED_UNITS); + if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); const maximumIncrements = { hour: 24, diff --git a/polyfill/lib/plaindate.mjs b/polyfill/lib/plaindate.mjs index 65c4275705..e61691f92e 100644 --- a/polyfill/lib/plaindate.mjs +++ b/polyfill/lib/plaindate.mjs @@ -18,6 +18,8 @@ import { HasSlot } from './slots.mjs'; +const DISALLOWED_UNITS = ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']; + export class PlainDate { constructor(isoYear, isoMonth, isoDay, calendar = ES.GetISO8601Calendar()) { isoYear = ES.ToInteger(isoYear); @@ -134,7 +136,7 @@ export class PlainDate { options = ES.GetOptionsObject(options); let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration; - ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days')); + ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'day')); duration = { years, months, weeks, days }; return ES.CalendarDateAdd(GetSlot(this, CALENDAR), this, duration, options); } @@ -145,7 +147,7 @@ export class PlainDate { options = ES.GetOptionsObject(options); let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration; - ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days')); + ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'day')); duration = { years: -years, months: -months, weeks: -weeks, days: -days }; return ES.CalendarDateAdd(GetSlot(this, CALENDAR), this, duration, options); } @@ -161,16 +163,16 @@ export class PlainDate { } options = ES.GetOptionsObject(options); - const disallowedUnits = ['hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'days', disallowedUnits); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit, disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'day', DISALLOWED_UNITS); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false); - const result = ES.CalendarDateUntil(calendar, this, other, options); - if (smallestUnit === 'days' && roundingIncrement === 1) return result; + const untilOptions = { ...options, largestUnit }; + const result = ES.CalendarDateUntil(calendar, this, other, untilOptions); + if (smallestUnit === 'day' && roundingIncrement === 1) return result; let { years, months, weeks, days } = result; const relativeTo = ES.CreateTemporalDateTime( @@ -217,17 +219,17 @@ export class PlainDate { } options = ES.GetOptionsObject(options); - const disallowedUnits = ['hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'days', disallowedUnits); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit, disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'day', DISALLOWED_UNITS); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false); - let { years, months, weeks, days } = ES.CalendarDateUntil(calendar, this, other, options); + const untilOptions = { ...options, largestUnit }; + let { years, months, weeks, days } = ES.CalendarDateUntil(calendar, this, other, untilOptions); const Duration = GetIntrinsic('%Temporal.Duration%'); - if (smallestUnit === 'days' && roundingIncrement === 1) { + if (smallestUnit === 'day' && roundingIncrement === 1) { return new Duration(-years, -months, -weeks, -days, 0, 0, 0, 0, 0, 0); } const relativeTo = ES.CreateTemporalDateTime( diff --git a/polyfill/lib/plaindatetime.mjs b/polyfill/lib/plaindatetime.mjs index 049be06253..0bc2df55f1 100644 --- a/polyfill/lib/plaindatetime.mjs +++ b/polyfill/lib/plaindatetime.mjs @@ -379,9 +379,9 @@ export class PlainDateTime { throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`); } options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond'); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit); @@ -473,9 +473,9 @@ export class PlainDateTime { throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`); } options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('days', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond'); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit); @@ -571,7 +571,8 @@ export class PlainDateTime { if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver'); if (options === undefined) throw new TypeError('options parameter is required'); options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options); + const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week']); + if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); const maximumIncrements = { day: 1, diff --git a/polyfill/lib/plaintime.mjs b/polyfill/lib/plaintime.mjs index ab56d1ee14..df1c2407a5 100644 --- a/polyfill/lib/plaintime.mjs +++ b/polyfill/lib/plaintime.mjs @@ -25,6 +25,16 @@ import { const ObjectAssign = Object.assign; +const DISALLOWED_UNITS = ['year', 'month', 'week', 'day']; +const MAX_INCREMENTS = { + hour: 24, + minute: 60, + second: 60, + millisecond: 1000, + microsecond: 1000, + nanosecond: 1000 +}; + function TemporalTimeToString(time, precision, options = undefined) { let hour = GetSlot(time, ISO_HOUR); let minute = GetSlot(time, ISO_MINUTE); @@ -228,19 +238,11 @@ export class PlainTime { if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver'); other = ES.ToTemporalTime(other); options = ES.GetOptionsObject(options); - const largestUnit = ES.ToLargestTemporalUnit(options, 'hours', ['years', 'months', 'weeks', 'days']); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'hour'); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); - const maximumIncrements = { - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_INCREMENTS[smallestUnit], false); let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceTime( GetSlot(this, ISO_HOUR), GetSlot(this, ISO_MINUTE), @@ -287,19 +289,11 @@ export class PlainTime { if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver'); other = ES.ToTemporalTime(other); options = ES.GetOptionsObject(options); - const largestUnit = ES.ToLargestTemporalUnit(options, 'hours', ['years', 'months', 'weeks', 'days']); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'hour'); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); - const maximumIncrements = { - hours: 24, - minutes: 60, - seconds: 60, - milliseconds: 1000, - microseconds: 1000, - nanoseconds: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_INCREMENTS[smallestUnit], false); let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceTime( GetSlot(other, ISO_HOUR), GetSlot(other, ISO_MINUTE), @@ -352,17 +346,10 @@ export class PlainTime { if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver'); if (options === undefined) throw new TypeError('options parameter is required'); options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, ['day']); + const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, DISALLOWED_UNITS); + if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); - const maximumIncrements = { - hour: 24, - minute: 60, - second: 60, - millisecond: 1000, - microsecond: 1000, - nanosecond: 1000 - }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_INCREMENTS[smallestUnit], false); let hour = GetSlot(this, ISO_HOUR); let minute = GetSlot(this, ISO_MINUTE); diff --git a/polyfill/lib/plainyearmonth.mjs b/polyfill/lib/plainyearmonth.mjs index 4f23ad6ee8..484f41b96d 100644 --- a/polyfill/lib/plainyearmonth.mjs +++ b/polyfill/lib/plainyearmonth.mjs @@ -5,6 +5,8 @@ import { ISO_YEAR, ISO_MONTH, ISO_DAY, CALENDAR, TIME_ZONE, GetSlot, HasSlot } f const ObjectCreate = Object.create; +const DISALLOWED_UNITS = ['week', 'day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']; + export class PlainYearMonth { constructor(isoYear, isoMonth, calendar = ES.GetISO8601Calendar(), referenceISODay = 1) { isoYear = ES.ToInteger(isoYear); @@ -94,7 +96,7 @@ export class PlainYearMonth { if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver'); const duration = ES.ToLimitedTemporalDuration(temporalDurationLike); let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration; - ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days')); + ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'day')); options = ES.GetOptionsObject(options); @@ -125,7 +127,7 @@ export class PlainYearMonth { nanoseconds: -duration.nanoseconds }; let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration; - ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days')); + ({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'day')); options = ES.GetOptionsObject(options); @@ -153,18 +155,8 @@ export class PlainYearMonth { ); } options = ES.GetOptionsObject(options); - const disallowedUnits = [ - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]; - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'months', disallowedUnits); - const largestUnit = ES.ToLargestTemporalUnit(options, 'years', disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'month', DISALLOWED_UNITS); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'year'); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false); @@ -177,7 +169,7 @@ export class PlainYearMonth { const untilOptions = { ...options, largestUnit }; const result = ES.CalendarDateUntil(calendar, thisDate, otherDate, untilOptions); - if (smallestUnit === 'months' && roundingIncrement === 1) return result; + if (smallestUnit === 'month' && roundingIncrement === 1) return result; let { years, months } = result; const relativeTo = ES.CreateTemporalDateTime( @@ -225,18 +217,8 @@ export class PlainYearMonth { ); } options = ES.GetOptionsObject(options); - const disallowedUnits = [ - 'weeks', - 'days', - 'hours', - 'minutes', - 'seconds', - 'milliseconds', - 'microseconds', - 'nanoseconds' - ]; - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'months', disallowedUnits); - const largestUnit = ES.ToLargestTemporalUnit(options, 'years', disallowedUnits); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'month', DISALLOWED_UNITS); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'year'); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false); @@ -250,7 +232,7 @@ export class PlainYearMonth { const untilOptions = { ...options, largestUnit }; let { years, months } = ES.CalendarDateUntil(calendar, thisDate, otherDate, untilOptions); const Duration = GetIntrinsic('%Temporal.Duration%'); - if (smallestUnit === 'months' && roundingIncrement === 1) { + if (smallestUnit === 'month' && roundingIncrement === 1) { return new Duration(-years, -months, 0, 0, 0, 0, 0, 0, 0, 0); } const relativeTo = ES.CreateTemporalDateTime( diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index 850dd5e099..f001f78db1 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -358,9 +358,9 @@ export class ZonedDateTime { throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`); } options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('hours', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond'); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('hour', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit); @@ -368,7 +368,7 @@ export class ZonedDateTime { const ns1 = GetSlot(this, EPOCHNANOSECONDS); const ns2 = GetSlot(other, EPOCHNANOSECONDS); let years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds; - if (largestUnit !== 'years' && largestUnit !== 'months' && largestUnit !== 'weeks' && largestUnit !== 'days') { + if (largestUnit !== 'year' && largestUnit !== 'month' && largestUnit !== 'week' && largestUnit !== 'day') { // The user is only asking for a time difference, so return difference of instants. years = 0; months = 0; @@ -482,9 +482,9 @@ export class ZonedDateTime { throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`); } options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalDurationUnit(options, 'nanoseconds'); - const defaultLargestUnit = ES.LargerOfTwoTemporalDurationUnits('hours', smallestUnit); - const largestUnit = ES.ToLargestTemporalUnit(options, defaultLargestUnit); + const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond'); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('hour', smallestUnit); + const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit); ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc'); roundingMode = ES.NegateTemporalRoundingMode(roundingMode); @@ -493,7 +493,7 @@ export class ZonedDateTime { const ns1 = GetSlot(this, EPOCHNANOSECONDS); const ns2 = GetSlot(other, EPOCHNANOSECONDS); let years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds; - if (largestUnit !== 'years' && largestUnit !== 'months' && largestUnit !== 'weeks' && largestUnit !== 'days') { + if (largestUnit !== 'year' && largestUnit !== 'month' && largestUnit !== 'week' && largestUnit !== 'day') { // The user is only asking for a time difference, so return difference of instants. years = 0; months = 0; @@ -611,7 +611,8 @@ export class ZonedDateTime { if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver'); if (options === undefined) throw new TypeError('options parameter is required'); options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options); + const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week']); + if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); const maximumIncrements = { day: 1, diff --git a/polyfill/test/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js b/polyfill/test/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..fc20816033 --- /dev/null +++ b/polyfill/test/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js @@ -0,0 +1,19 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.calendar.prototype.dateuntil +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDate(2000, 5, 2); +const later = new Temporal.PlainDate(2001, 6, 12); +const calendar = new Temporal.Calendar("iso8601"); +const validUnits = [ + "year", + "month", + "week", + "day", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => calendar.dateUntil(earlier, later, { largestUnit }), validUnits); diff --git a/polyfill/test/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..31e4a7b6c5 --- /dev/null +++ b/polyfill/test/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,77 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.add step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-addduration steps 6-7: + 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*). + l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). + m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_). + ... + 7. Else, + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + one.add(two, { relativeTo, largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + one.add(two, { relativeTo, largestUnit }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); diff --git a/polyfill/test/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..08fd6f98a3 --- /dev/null +++ b/polyfill/test/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,159 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.round steps 20–25: + 20. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_). + 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 22. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 23. Let _balanceResult_ be ? BalanceDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _largestUnit_, _relativeTo_). + 24. ... + 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult.[[Nanoseconds]], _largestUnit_, _relativeTo_). + sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v: + 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"month"*, then + ... + d. Repeat, while abs(_years_) > 0, + ... + iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-balancedurationrelative steps 1, 9.m–o, and 9.q.vi–viii: + 1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"year"*, then + ... + m. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + n. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + o. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + p. ... + q. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_), + ... + vi. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + vii. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + viii. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-balanceduration step 3.a: + 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +// Check with smallestUnit nanoseconds but roundingIncrement > 1; each call +// should result in two calls to dateUntil() originating from +// AdjustRoundedDurationDays, one with largestUnit equal to the largest unit in +// the duration higher than "day", and one with largestUnit: "day". +// Additionally one call with largestUnit: "month" in BalanceDurationRelative +// when the largestUnit given to round() is "year", and one call with +// largestUnit: "day" when the largestUnit given to round() is "year", "month", +// "week", or "day". + +const durations = [ + [1, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], +].map((args) => new Temporal.Duration(...args)); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const duration = durations[index]; + const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar); + duration.round({ largestUnit, roundingIncrement: 2, roundingMode: 'ceil', relativeTo }); + }, + { + years: ["year", "day", "month", "day"], + months: ["month", "day", "day"], + weeks: ["week", "day", "day"], + days: ["day", "day", "day"], + hours: ["day", "day"], + minutes: ["day", "day"], + seconds: ["day", "day"], + milliseconds: ["day", "day"], + microseconds: ["day", "day"], + nanoseconds: ["day", "day"] + } +); + +// Check the path that converts months to years and vice versa in +// BalanceDurationRelative and UnbalanceDurationRelative. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const duration = new Temporal.Duration(5, 60); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar); + duration.round({ largestUnit, relativeTo }); + }, + { + years: ["month", "month", "month", "month", "month", "month"], + months: ["month", "month", "month", "month", "month"], + weeks: [], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Check the paths that call dateUntil() in RoundDuration. These paths do not +// call dateUntil() in AdjustRoundedDurationDays. Note that there is no +// largestUnit: "month" call in BalanceDurationRelative and no largestUnit: +// "day" call in BalanceDuration, because the durations have rounded down to 0. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + duration.round({ largestUnit, smallestUnit: largestUnit, relativeTo }); + }, { + years: ["day", "year"], + months: ["day"], + weeks: ["day"], + days: ["day"] + } +); diff --git a/polyfill/test/Duration/prototype/round/largestunit-plurals-accepted.js b/polyfill/test/Duration/prototype/round/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..fce9457ef0 --- /dev/null +++ b/polyfill/test/Duration/prototype/round/largestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => duration.round({ largestUnit, relativeTo }), validUnits); diff --git a/polyfill/test/Duration/prototype/round/smallestunit-plurals-accepted.js b/polyfill/test/Duration/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..fb44b877f5 --- /dev/null +++ b/polyfill/test/Duration/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round({ smallestUnit, relativeTo }), validUnits); diff --git a/polyfill/test/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..abfbd4df28 --- /dev/null +++ b/polyfill/test/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,77 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.subtract step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-addduration steps 6-7: + 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*). + l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). + m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_). + ... + 7. Else, + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + two.subtract(one, { relativeTo }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + two.subtract(one, { relativeTo }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); diff --git a/polyfill/test/Duration/prototype/toString/smallestunit-plurals-accepted.js b/polyfill/test/Duration/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..41231eaed1 --- /dev/null +++ b/polyfill/test/Duration/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,17 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.toString({ smallestUnit }), validUnits); diff --git a/polyfill/test/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..f692e65455 --- /dev/null +++ b/polyfill/test/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,92 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.total steps 7–11: + 7. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_). + ... + 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_). + 11. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_). + sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v: + 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"month"*, then + ... + d. Repeat, while abs(_years_) > 0, + ... + iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-balanceduration step 3.a: + 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +// Check the paths that go through NanosecondsToDays: one call to dateUntil() in +// BalanceDuration and one in RoundDuration with largestUnit: "day" when the +// unit is "year", "month", "week", or "day", and one extra call with +// largestUnit: "year" in RoundDuration when the unit is "year". + +const duration = new Temporal.Duration(0, 1, 1, 1, 1, 1, 1, 1, 1, 1); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, unit) => { + const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar); + duration.total({ unit, relativeTo }); + }, + { + years: ["day", "day", "year"], + months: ["day", "day"], + weeks: ["day", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Check the path that converts years to months in UnbalanceDurationRelative. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, unit) => { + const duration = new Temporal.Duration(5); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar); + duration.total({ unit, relativeTo }); + }, + { + years: ["year"], + months: ["month", "month", "month", "month", "month"], + weeks: [], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); diff --git a/polyfill/test/Duration/prototype/total/unit-plurals-accepted.js b/polyfill/test/Duration/prototype/total/unit-plurals-accepted.js new file mode 100644 index 0000000000..2256d7d3ae --- /dev/null +++ b/polyfill/test/Duration/prototype/total/unit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Plural units are accepted as well for the unit option +includes: [temporalHelpers.js] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total({ unit, relativeTo }), validUnits); diff --git a/polyfill/test/Instant/prototype/round/smallestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..aee59e4455 --- /dev/null +++ b/polyfill/test/Instant/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,19 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const instant = new Temporal.Instant(1_000_000_000_987_654_321n); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.round({ smallestUnit }), validUnits); diff --git a/polyfill/test/Instant/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..214560e19b --- /dev/null +++ b/polyfill/test/Instant/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.Instant(1_000_000_000_987_654_321n); +const later = new Temporal.Instant(1_086_403_661_988_655_322n); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/Instant/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..d7a23842fe --- /dev/null +++ b/polyfill/test/Instant/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.Instant(1_000_000_000_987_654_321n); +const later = new Temporal.Instant(1_086_403_661_988_655_322n); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/Instant/prototype/toString/smallestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..20aea9f551 --- /dev/null +++ b/polyfill/test/Instant/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const instant = new Temporal.Instant(1_000_000_000_123_456_789n); +const validUnits = [ + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.toString({ smallestUnit }), validUnits); diff --git a/polyfill/test/Instant/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..eb95ee98c5 --- /dev/null +++ b/polyfill/test/Instant/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.Instant(1_000_000_000_987_654_321n); +const later = new Temporal.Instant(1_086_403_661_988_655_322n); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/Instant/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/Instant/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..694a0317fa --- /dev/null +++ b/polyfill/test/Instant/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.Instant(1_000_000_000_987_654_321n); +const later = new Temporal.Instant(1_086_403_661_988_655_322n); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..6896875135 --- /dev/null +++ b/polyfill/test/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,26 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plaindate.prototype.since steps 13–14: + 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_). + 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _other_, _temporalDate_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainDate(2000, 5, 2, calendar); + const later = new Temporal.PlainDate(2001, 6, 3, calendar); + later.since(earlier, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"] + } +); diff --git a/polyfill/test/PlainDate/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/PlainDate/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..9892e88c7d --- /dev/null +++ b/polyfill/test/PlainDate/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDate(2000, 5, 2); +const later = new Temporal.PlainDate(2001, 6, 12); +const validUnits = [ + "year", + "month", + "week", + "day", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainDate/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/PlainDate/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..cd03930bf2 --- /dev/null +++ b/polyfill/test/PlainDate/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDate(2000, 5, 2); +const later = new Temporal.PlainDate(2001, 6, 12); +const validUnits = [ + "year", + "month", + "week", + "day", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..2f37866846 --- /dev/null +++ b/polyfill/test/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,26 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plaindate.prototype.until steps 12–13: + 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_). + 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _temporalDate_, _other_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainDate(2000, 5, 2, calendar); + const later = new Temporal.PlainDate(2001, 6, 3, calendar); + earlier.until(later, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"] + } +); diff --git a/polyfill/test/PlainDate/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/PlainDate/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..5c5e2d0c15 --- /dev/null +++ b/polyfill/test/PlainDate/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDate(2000, 5, 2); +const later = new Temporal.PlainDate(2001, 6, 12); +const validUnits = [ + "year", + "month", + "week", + "day", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainDate/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/PlainDate/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..27af2b3cfc --- /dev/null +++ b/polyfill/test/PlainDate/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDate(2000, 5, 2); +const later = new Temporal.PlainDate(2001, 6, 12); +const validUnits = [ + "year", + "month", + "week", + "day", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..4e522a6e2e --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..759f106532 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,35 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plaindatetime.prototype.since step 14: + 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar); + later.since(earlier, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); diff --git a/polyfill/test/PlainDateTime/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..3ab52eec93 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..58cd297d20 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..cc88ffe553 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999); +const validUnits = [ + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..ffbc78b414 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,35 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plaindatetime.prototype.until step 13: + 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar); + earlier.until(later, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); diff --git a/polyfill/test/PlainDateTime/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..28527669fc --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..f80b117c97 --- /dev/null +++ b/polyfill/test/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/round/smallestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..d9d6bb562e --- /dev/null +++ b/polyfill/test/PlainTime/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,19 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.round({ smallestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..1428496969 --- /dev/null +++ b/polyfill/test/PlainTime/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..3e18fd31cd --- /dev/null +++ b/polyfill/test/PlainTime/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/toString/smallestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..87afeba754 --- /dev/null +++ b/polyfill/test/PlainTime/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999); +const validUnits = [ + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.toString({ smallestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..00312042d9 --- /dev/null +++ b/polyfill/test/PlainTime/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainTime/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/PlainTime/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..b89cf0a5e4 --- /dev/null +++ b/polyfill/test/PlainTime/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); +const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322); +const validUnits = [ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..77e43c2ffc --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.since +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plainyearmonth.prototype.since steps 21–22: + 21. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_). + 22. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainYearMonth(2000, 5, calendar); + const later = new Temporal.PlainYearMonth(2001, 6, calendar); + later.since(earlier, { largestUnit }); + }, + { + years: ["year"], + months: ["month"] + } +); diff --git a/polyfill/test/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..d1c4c600ca --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainYearMonth(2000, 5); +const later = new Temporal.PlainYearMonth(2001, 6); +const validUnits = [ + "year", + "month", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..112d7c89b6 --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainYearMonth(2000, 5); +const later = new Temporal.PlainYearMonth(2001, 6); +const validUnits = [ + "year", + "month", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..269534a111 --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.until +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.plainyearmonth.prototype.until steps 20–21: + 20. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_). + 21. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.PlainYearMonth(2000, 5, calendar); + const later = new Temporal.PlainYearMonth(2001, 6, calendar); + earlier.until(later, { largestUnit }); + }, + { + years: ["year"], + months: ["month"] + } +); diff --git a/polyfill/test/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..d6977ce3ca --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainYearMonth(2000, 5); +const later = new Temporal.PlainYearMonth(2001, 6); +const validUnits = [ + "year", + "month", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..dfb9b1ec52 --- /dev/null +++ b/polyfill/test/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.PlainYearMonth(2000, 5); +const later = new Temporal.PlainYearMonth(2001, 6); +const validUnits = [ + "year", + "month", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..fc8a3c3bba --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,20 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..c7b6d85227 --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,113 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.zoneddatetime.prototype.since steps 14–18: + 14. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + c. Return ... + 15. ... + 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). + 17. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + 18. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + later.since(earlier, { largestUnit }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Additionally check the path that goes through AdjustRoundedDurationDays + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(0n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar); + later.since(earlier, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' }); + }, + { + years: ["year", "day", "day", "day"], + months: ["month", "day", "day", "day"], + weeks: ["week", "day", "day", "day"], + days: ["day", "day", "day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Also check the path that goes through RoundDuration when smallestUnit is +// given + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, smallestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + later.since(earlier, { smallestUnit }); + }, + { + years: ["year", "day", "day", "year"], + months: ["month", "day", "day"], + weeks: ["week", "day", "day"], + days: ["day", "day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); diff --git a/polyfill/test/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..73cb57fc69 --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..3306a1e2d5 --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..f6acc0c415 --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC"); +const validUnits = [ + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/polyfill/test/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..be74fdfecc --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,113 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.zoneddatetime.prototype.until steps 13–17: + 13. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + c. Return ... + 14. ... + 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). + 16. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + 17. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + earlier.until(later, { largestUnit }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Additionally check the path that goes through AdjustRoundedDurationDays + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(0n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar); + earlier.until(later, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' }); + }, + { + years: ["year", "day", "day", "day"], + months: ["month", "day", "day", "day"], + weeks: ["week", "day", "day", "day"], + days: ["day", "day", "day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Also check the path that goes through RoundDuration when smallestUnit is +// given + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, smallestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + earlier.until(later, { smallestUnit }); + }, + { + years: ["year", "day", "day", "year"], + months: ["month", "day", "day"], + weeks: ["week", "day", "day"], + days: ["day", "day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); diff --git a/polyfill/test/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..7130d7922f --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); diff --git a/polyfill/test/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js b/polyfill/test/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..f1d63f619f --- /dev/null +++ b/polyfill/test/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,24 @@ +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); diff --git a/polyfill/test/helpers/temporalHelpers.js b/polyfill/test/helpers/temporalHelpers.js index ebd34c8f9c..a3cc9ed266 100644 --- a/polyfill/test/helpers/temporalHelpers.js +++ b/polyfill/test/helpers/temporalHelpers.js @@ -111,6 +111,48 @@ var TemporalHelpers = { assert.sameValue(yearMonth.monthCode, monthCode, `${description} monthCode result`); }, + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0, actual.length); // empty it for the next check + }); + }, + /* * checkPlainDateTimeConversionFastPath(func): * @@ -160,6 +202,56 @@ var TemporalHelpers = { assert.compareArray(actual, expected, "property getters not called"); }, + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + if (singularValue instanceof Temporal.Duration) { + assert.sameValue(pluralValue.years, singularValue.years, "years value"); + assert.sameValue(pluralValue.months, singularValue.months, "months value"); + assert.sameValue(pluralValue.weeks, singularValue.weeks, "weeks value"); + assert.sameValue(pluralValue.days, singularValue.days, "days value"); + assert.sameValue(pluralValue.hours, singularValue.hours, "hours value"); + assert.sameValue(pluralValue.minutes, singularValue.minutes, "minutes value"); + assert.sameValue(pluralValue.seconds, singularValue.seconds, "seconds value"); + assert.sameValue(pluralValue.milliseconds, singularValue.milliseconds, "milliseconds value"); + assert.sameValue(pluralValue.microseconds, singularValue.microseconds, "microseconds value"); + assert.sameValue(pluralValue.nanoseconds, singularValue.nanoseconds, "nanoseconds value"); + } else if ( + singularValue instanceof Temporal.Instant || + singularValue instanceof Temporal.PlainDateTime || + singularValue instanceof Temporal.PlainTime || + singularValue instanceof Temporal.ZonedDateTime + ) { + assert(pluralValue.equals(singularValue), "Temporal objects equal"); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + /* * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, * resultAssertions):