From 1b3d0186dcd202431bd455bf8e76b94e62698c16 Mon Sep 17 00:00:00 2001
From: Richard Gibson
Date: Wed, 8 Jun 2022 13:33:33 -0400
Subject: [PATCH] Editorial: Simplify operations (#2248)
* Replace ToLargestTemporalUnit with a more general GetTemporalUnit
* Replace ToSmallestTemporalUnit with GetTemporalUnit
* Replace ToTemporalDurationTotalUnit with GetTemporalUnit
* Use the units table to support more operations
---
polyfill/lib/calendar.mjs | 8 +-
polyfill/lib/duration.mjs | 11 ++-
polyfill/lib/ecmascript.mjs | 175 ++++++++++++++++++---------------
polyfill/lib/instant.mjs | 4 +-
polyfill/lib/plaindatetime.mjs | 3 +-
polyfill/lib/plaintime.mjs | 4 +-
polyfill/lib/zoneddatetime.mjs | 3 +-
spec/abstractops.html | 165 +++++++++++--------------------
spec/calendar.html | 3 +-
spec/duration.html | 8 +-
spec/instant.html | 10 +-
spec/intl.html | 3 +-
spec/plaindate.html | 8 +-
spec/plaindatetime.html | 10 +-
spec/plaintime.html | 10 +-
spec/plainyearmonth.html | 10 +-
spec/zoneddatetime.html | 10 +-
17 files changed, 204 insertions(+), 241 deletions(-)
diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs
index 7efcd94e26..d3c286dcc9 100644
--- a/polyfill/lib/calendar.mjs
+++ b/polyfill/lib/calendar.mjs
@@ -137,12 +137,8 @@ export class Calendar {
one = ES.ToTemporalDate(one);
two = ES.ToTemporalDate(two);
options = ES.GetOptionsObject(options);
- const largestUnit = ES.ToLargestTemporalUnit(
- options,
- 'auto',
- ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'],
- 'day'
- );
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'date', 'auto');
+ if (largestUnit === 'auto') largestUnit = '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);
diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs
index 8e8ee629f3..27ed34976e 100644
--- a/polyfill/lib/duration.mjs
+++ b/polyfill/lib/duration.mjs
@@ -230,14 +230,14 @@ export class Duration {
} else {
roundTo = ES.GetOptionsObject(roundTo);
}
- let smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined);
+ let smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'datetime', undefined);
let smallestUnitPresent = true;
if (!smallestUnit) {
smallestUnitPresent = false;
smallestUnit = 'nanosecond';
}
defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit);
- let largestUnit = ES.ToLargestTemporalUnit(roundTo, undefined);
+ let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
let largestUnitPresent = true;
if (!largestUnit) {
largestUnitPresent = false;
@@ -247,7 +247,9 @@ export class Duration {
if (!smallestUnitPresent && !largestUnitPresent) {
throw new RangeError('at least one of smallestUnit or largestUnit is required');
}
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(roundTo, smallestUnit);
let relativeTo = ES.ToRelativeTemporalObject(roundTo);
@@ -333,8 +335,7 @@ export class Duration {
} else {
totalOf = ES.GetOptionsObject(totalOf);
}
- const unit = ES.ToTemporalDurationTotalUnit(totalOf, undefined);
- if (unit === undefined) throw new RangeError('unit option is required');
+ const unit = ES.GetTemporalUnit(totalOf, 'unit', 'datetime', ES.REQUIRED);
const relativeTo = ES.ToRelativeTemporalObject(totalOf);
// Convert larger units down to days
diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs
index e0617de269..ae1a3c35f0 100644
--- a/polyfill/lib/ecmascript.mjs
+++ b/polyfill/lib/ecmascript.mjs
@@ -155,30 +155,22 @@ const BUILTIN_CASTS = new Map([
['offset', ToString]
]);
-const ALLOWED_UNITS = [
- 'year',
- 'month',
- 'week',
- 'day',
- 'hour',
- 'minute',
- 'second',
- 'millisecond',
- 'microsecond',
- 'nanosecond'
-];
+// each item is [plural, singular, category]
const SINGULAR_PLURAL_UNITS = [
- ['years', 'year'],
- ['months', 'month'],
- ['weeks', 'week'],
- ['days', 'day'],
- ['hours', 'hour'],
- ['minutes', 'minute'],
- ['seconds', 'second'],
- ['milliseconds', 'millisecond'],
- ['microseconds', 'microsecond'],
- ['nanoseconds', 'nanosecond']
+ ['years', 'year', 'date'],
+ ['months', 'month', 'date'],
+ ['weeks', 'week', 'date'],
+ ['days', 'day', 'date'],
+ ['hours', 'hour', 'time'],
+ ['minutes', 'minute', 'time'],
+ ['seconds', 'second', 'time'],
+ ['milliseconds', 'millisecond', 'time'],
+ ['microseconds', 'microsecond', 'time'],
+ ['nanoseconds', 'nanosecond', 'time']
];
+const SINGULAR_FOR = new Map(SINGULAR_PLURAL_UNITS);
+const PLURAL_FOR = new Map(SINGULAR_PLURAL_UNITS.map(([p, s]) => [s, p]));
+const UNITS_DESCENDING = SINGULAR_PLURAL_UNITS.map(([, s]) => s);
import * as PARSE from './regex.mjs';
@@ -691,7 +683,14 @@ export const ES = ObjectAssign({}, ES2020, {
return ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false);
},
ToSecondsStringPrecision: (options) => {
- let smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week', 'day', 'hour']);
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
+ if (smallestUnit === 'hour') {
+ const ALLOWED_UNITS = SINGULAR_PLURAL_UNITS.reduce((allowed, [p, s, c]) => {
+ if (c === 'time' && s !== 'hour') allowed.push(s, p);
+ return allowed;
+ }, []);
+ throw new RangeError(`smallestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${smallestUnit}`);
+ }
switch (smallestUnit) {
case 'minute':
return { precision: 'minute', unit: 'minute', increment: 1 };
@@ -733,37 +732,33 @@ export const ES = ObjectAssign({}, ES2020, {
return { precision, unit: 'nanosecond', increment: 10 ** (9 - precision) };
}
},
- 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);
+ REQUIRED: Symbol('~required~'),
+ GetTemporalUnit: (options, key, unitGroup, requiredOrDefault, extraValues = []) => {
+ const allowedSingular = [];
+ for (const [, singular, category] of SINGULAR_PLURAL_UNITS) {
+ if (unitGroup === 'datetime' || unitGroup === category) {
+ allowedSingular.push(singular);
+ }
+ }
+ allowedSingular.push(...extraValues);
+ let defaultVal = requiredOrDefault;
+ if (defaultVal === ES.REQUIRED) {
+ defaultVal = undefined;
+ } else if (defaultVal !== undefined) {
+ allowedSingular.push(defaultVal);
+ }
+ const allowedValues = [...allowedSingular];
+ for (const singular of allowedSingular) {
+ const plural = PLURAL_FOR.get(singular);
+ if (plural !== undefined) allowedValues.push(plural);
+ }
+ let retval = ES.GetOption(options, key, allowedValues, defaultVal);
+ if (retval === undefined && requiredOrDefault === ES.REQUIRED) {
+ throw new RangeError(`${key} is required`);
}
- 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);
+ if (SINGULAR_FOR.has(retval)) retval = SINGULAR_FOR.get(retval);
return retval;
},
- 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()], fallback);
- if (singular.has(value)) return singular.get(value);
- return value;
- },
- ToTemporalDurationTotalUnit: (options) => {
- // 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 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) => {
const relativeTo = options.relativeTo;
if (relativeTo === undefined) return relativeTo;
@@ -834,11 +829,6 @@ export const ES = ObjectAssign({}, ES2020, {
}
return ES.CreateTemporalDate(year, month, day, calendar);
},
- ValidateTemporalUnitRange: (largestUnit, smallestUnit) => {
- if (ALLOWED_UNITS.indexOf(largestUnit) > ALLOWED_UNITS.indexOf(smallestUnit)) {
- throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
- }
- },
DefaultTemporalLargestUnit: (
years,
months,
@@ -851,7 +841,6 @@ export const ES = ObjectAssign({}, ES2020, {
microseconds,
nanoseconds
) => {
- const singular = new Map(SINGULAR_PLURAL_UNITS);
for (const [prop, v] of ObjectEntries({
years,
months,
@@ -864,12 +853,12 @@ export const ES = ObjectAssign({}, ES2020, {
microseconds,
nanoseconds
})) {
- if (v !== 0) return singular.get(prop);
+ if (v !== 0) return SINGULAR_FOR.get(prop);
}
return 'nanosecond';
},
LargerOfTwoTemporalUnits: (unit1, unit2) => {
- if (ALLOWED_UNITS.indexOf(unit1) > ALLOWED_UNITS.indexOf(unit2)) return unit2;
+ if (UNITS_DESCENDING.indexOf(unit1) > UNITS_DESCENDING.indexOf(unit2)) return unit2;
return unit1;
},
MergeLargestUnitOption: (options, largestUnit) => {
@@ -3309,11 +3298,13 @@ export const ES = ObjectAssign({}, ES2020, {
[first, second] = [other, instant];
}
options = ES.GetOptionsObject(options);
- const DISALLOWED_UNITS = ['year', 'month', 'week', 'day'];
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS);
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', 'nanosecond');
const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('second', smallestUnit);
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit);
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'time', 'auto');
+ if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const MAX_DIFFERENCE_INCREMENTS = {
hour: 24,
@@ -3359,11 +3350,13 @@ export const ES = ObjectAssign({}, ES2020, {
}
options = ES.GetOptionsObject(options);
- const DISALLOWED_UNITS = ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'];
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'day', DISALLOWED_UNITS);
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'date', 'day');
const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit);
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, defaultLargestUnit);
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'date', 'auto');
+ if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
if (operation === 'since') roundingMode = ES.NegateTemporalRoundingMode(roundingMode);
const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false);
@@ -3404,10 +3397,13 @@ export const ES = ObjectAssign({}, ES2020, {
throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`);
}
options = ES.GetOptionsObject(options);
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond');
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'datetime', 'nanosecond');
const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('day', smallestUnit);
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit);
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'datetime', 'auto');
+ if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
if (operation === 'since') roundingMode = ES.NegateTemporalRoundingMode(roundingMode);
const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit);
@@ -3484,10 +3480,12 @@ export const ES = ObjectAssign({}, ES2020, {
const sign = operation === 'since' ? -1 : 1;
other = ES.ToTemporalTime(other);
options = ES.GetOptionsObject(options);
- const DISALLOWED_UNITS = ['year', 'month', 'week', 'day'];
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'hour');
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond', DISALLOWED_UNITS);
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'time', 'auto');
+ if (largestUnit === 'auto') largestUnit = 'hour';
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', 'nanosecond');
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
if (operation === 'since') roundingMode = ES.NegateTemporalRoundingMode(roundingMode);
const MAX_INCREMENTS = {
@@ -3565,10 +3563,22 @@ export const ES = ObjectAssign({}, ES2020, {
);
}
options = ES.GetOptionsObject(options);
- const DISALLOWED_UNITS = ['week', 'day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'];
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'month', DISALLOWED_UNITS);
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', DISALLOWED_UNITS, 'year');
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ const ALLOWED_UNITS = SINGULAR_PLURAL_UNITS.reduce((allowed, [p, s, c]) => {
+ if (c === 'date' && s !== 'week' && s !== 'day') allowed.push(s, p);
+ return allowed;
+ }, []);
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'date', 'month');
+ if (smallestUnit === 'week' || smallestUnit === 'day') {
+ throw new RangeError(`smallestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${smallestUnit}`);
+ }
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'date', 'auto');
+ if (largestUnit === 'week' || largestUnit === 'day') {
+ throw new RangeError(`largestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${largestUnit}`);
+ }
+ if (largestUnit === 'auto') largestUnit = 'year';
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
if (operation === 'since') roundingMode = ES.NegateTemporalRoundingMode(roundingMode);
const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false);
@@ -3615,10 +3625,13 @@ export const ES = ObjectAssign({}, ES2020, {
throw new RangeError(`cannot compute difference between dates of ${calendarId} and ${otherCalendarId} calendars`);
}
options = ES.GetOptionsObject(options);
- const smallestUnit = ES.ToSmallestTemporalUnit(options, 'nanosecond');
+ const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'datetime', 'nanosecond');
const defaultLargestUnit = ES.LargerOfTwoTemporalUnits('hour', smallestUnit);
- const largestUnit = ES.ToLargestTemporalUnit(options, 'auto', [], defaultLargestUnit);
- ES.ValidateTemporalUnitRange(largestUnit, smallestUnit);
+ let largestUnit = ES.GetTemporalUnit(options, 'largestUnit', 'datetime', 'auto');
+ if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
+ if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
+ throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
+ }
let roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
if (operation === 'since') roundingMode = ES.NegateTemporalRoundingMode(roundingMode);
const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit);
diff --git a/polyfill/lib/instant.mjs b/polyfill/lib/instant.mjs
index 7bc639e6ac..8c60024e1f 100644
--- a/polyfill/lib/instant.mjs
+++ b/polyfill/lib/instant.mjs
@@ -79,9 +79,7 @@ export class Instant {
} else {
roundTo = ES.GetOptionsObject(roundTo);
}
- const DISALLOWED_UNITS = ['year', 'month', 'week', 'day'];
- const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, DISALLOWED_UNITS);
- if (smallestUnit === undefined) throw new RangeError('smallestUnit is required');
+ const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const maximumIncrements = {
hour: 24,
diff --git a/polyfill/lib/plaindatetime.mjs b/polyfill/lib/plaindatetime.mjs
index e05aa76aab..02de13d7c2 100644
--- a/polyfill/lib/plaindatetime.mjs
+++ b/polyfill/lib/plaindatetime.mjs
@@ -295,8 +295,7 @@ export class PlainDateTime {
} else {
roundTo = ES.GetOptionsObject(roundTo);
}
- const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, ['year', 'month', 'week']);
- if (smallestUnit === undefined) throw new RangeError('smallestUnit is required');
+ const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const maximumIncrements = {
day: 1,
diff --git a/polyfill/lib/plaintime.mjs b/polyfill/lib/plaintime.mjs
index 9d0f102610..f5d95efaad 100644
--- a/polyfill/lib/plaintime.mjs
+++ b/polyfill/lib/plaintime.mjs
@@ -171,9 +171,7 @@ export class PlainTime {
} else {
roundTo = ES.GetOptionsObject(roundTo);
}
- const DISALLOWED_UNITS = ['year', 'month', 'week', 'day'];
- const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, DISALLOWED_UNITS);
- if (smallestUnit === undefined) throw new RangeError('smallestUnit is required');
+ const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const MAX_INCREMENTS = {
hour: 24,
diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs
index 91d7020c49..3a010fb9df 100644
--- a/polyfill/lib/zoneddatetime.mjs
+++ b/polyfill/lib/zoneddatetime.mjs
@@ -350,8 +350,7 @@ export class ZonedDateTime {
} else {
roundTo = ES.GetOptionsObject(roundTo);
}
- const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, ['year', 'month', 'week']);
- if (smallestUnit === undefined) throw new RangeError('smallestUnit is required');
+ const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const maximumIncrements = {
day: 1,
diff --git a/spec/abstractops.html b/spec/abstractops.html
index e9b4d810f5..f05c5e7039 100644
--- a/spec/abstractops.html
+++ b/spec/abstractops.html
@@ -256,7 +256,8 @@ ToSecondsStringPrecision ( _normalizedOptions_ )
The precision may be an integer 0 through 9 signifying a number of digits after the decimal point in the seconds, the string *"minute"* signifying not to print seconds at all, or the string *"auto"* signifying to drop trailing zeroes after the decimal point.
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_normalizedOptions_, « *"year"*, *"month"*, *"week"*, *"day"*, *"hour"* », *undefined*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_normalizedOptions_, *"smallestUnit"*, ~time~, *undefined*).
+ 1. If _smallestUnit_ is *"hour"*, throw a *RangeError* exception.
1. If _smallestUnit_ is *"minute"*, then
1. Return the Record {
[[Precision]]: *"minute"*,
@@ -322,135 +323,118 @@ ToSecondsStringPrecision ( _normalizedOptions_ )
-
- ToLargestTemporalUnit ( _normalizedOptions_, _disallowedUnits_, _fallback_ [ , _autoValue_ ] )
-
- The abstract operation ToLargestTemporalUnit consults the `largestUnit` property of the Object _normalizedOptions_, and checks that it is a valid unit and not one of a List of _disallowedUnits_.
- It returns a _fallback_ value if the property is not present.
- Optionally, if an _autoValue_ is provided, then a value of *"auto"* is replaced with this value.
-
-
- Both singular and plural unit names are accepted, but only the singular form is returned.
-
-
- 1. Assert: _disallowedUnits_ does not contain _fallback_.
- 1. Assert: _disallowedUnits_ does not contain *"auto"*.
- 1. Assert: _autoValue_ is not present or _fallback_ is *"auto"*.
- 1. Assert: _autoValue_ is not present or _disallowedUnits_ does not contain _autoValue_.
- 1. Let _largestUnit_ be ? GetOption(_normalizedOptions_, *"largestUnit"*, « String », « *"auto"*, *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », _fallback_).
- 1. If _largestUnit_ is *"auto"* and _autoValue_ is present, then
- 1. Return _autoValue_.
- 1. If _largestUnit_ is in the Plural column of , then
- 1. Set _largestUnit_ to the corresponding Singular value of the same row.
- 1. If _disallowedUnits_ contains _largestUnit_, then
- 1. Throw a *RangeError* exception.
- 1. Return _largestUnit_.
-
-
-
-
- ToSmallestTemporalUnit ( _normalizedOptions_, _disallowedUnits_, _fallback_ )
-
- The abstract operation ToSmallestTemporalUnit consults the `smallestUnit` property of the Object _normalizedOptions_, and checks that it is a valid unit and not one of a List of _disallowedUnits_.
- It returns a _fallback_ value if the property is not present.
-
-
- Both singular and plural unit names are accepted, but only the singular form is returned.
-
-
- 1. Assert: _disallowedUnits_ does not contain _fallback_.
- 1. Let _smallestUnit_ be ? GetOption(_normalizedOptions_, *"smallestUnit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », _fallback_).
- 1. If _smallestUnit_ is in the Plural column of , then
- 1. Set _smallestUnit_ to the corresponding Singular value of the same row.
- 1. If _disallowedUnits_ contains _smallestUnit_, then
- 1. Throw a *RangeError* exception.
- 1. Return _smallestUnit_.
-
-
-
-
- ToTemporalDurationTotalUnit ( _normalizedOptions_ )
-
- The abstract operation ToTemporalDurationTotalUnit consults the `unit` property of the Object _normalizedOptions_, and checks that it is a valid unit.
- It throws if the property is not present.
-
+
+
+ GetTemporalUnit (
+ _normalizedOptions_: an Object,
+ _key_: a property key,
+ _unitGroup_: ~date~, ~time~, or ~datetime~,
+ _default_: ~required~ or an ECMAScript language value,
+ optional _extraValues_: a List of ECMAScript language values,
+ ): either a normal completion containing an ECMAScript language value, or an abrupt completion
+
+
- Both singular and plural unit names are accepted, but only the singular form is returned.
+ Both singular and plural unit names are accepted, but only the singular form is used internally.
- 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*).
- 1. If _unit_ is *undefined*, then
- 1. Throw a *RangeError* exception.
- 1. If _unit_ is in the Plural column of , then
- 1. Set _unit_ to the corresponding Singular value of the same row.
- 1. Return _unit_.
+ 1. Let _singularNames_ be a new empty List.
+ 1. For each row of , except the header row, in table order, do
+ 1. Let _unit_ be the value in the Singular column of the row.
+ 1. If the Category column of the row is ~date~ and _unitGroup_ is ~date~ or ~datetime~, append _unit_ to _singularNames_.
+ 1. Else if the Category column of the row is ~time~ and _unitGroup_ is ~time~ or ~datetime~, append _unit_ to _singularNames_.
+ 1. If _extraValues_ is present, then
+ 1. Set _singularNames_ to the list-concatenation of _singularNames_ and _extraValues_.
+ 1. If _default_ is ~required~, then
+ 1. Let _defaultValue_ be *undefined*.
+ 1. Else,
+ 1. Let _defaultValue_ be _default_.
+ 1. If _defaultValue_ is not *undefined* and _singularNames_ does not contain _defaultValue_, then
+ 1. Append _defaultValue_ to _singularNames_.
+ 1. Let _allowedValues_ be a copy of _singularNames_.
+ 1. For each element _singularName_ of _singularNames_, do
+ 1. If _singularName_ is listed in the Singular column of , then
+ 1. Let _pluralName_ be the value in the Plural column of the corresponding row.
+ 1. Append _pluralName_ to _allowedValues_.
+ 1. NOTE: For each singular Temporal unit name that is contained within _allowedValues_, the corresponding plural name is also contained within it.
+ 1. Let _value_ be ? GetOption(_normalizedOptions_, _key_, « String », _allowedValues_, _defaultValue_).
+ 1. If _value_ is *undefined* and _default_ is ~required~, throw a *RangeError* exception.
+ 1. If _value_ is listed in the Plural column of , then
+ 1. Set _value_ to the value in the Singular column of the corresponding row.
+ 1. Return _value_.
-
-
-
- Singular and plural units
-
- Where possible, the `smallestUnit`, `largestUnit`, and `unit` options accept both singular and plural forms.
- Only the singular form is used internally.
-
-
- Singular and plural units
+
+ Temporal units by descending magnitude
Singular |
Plural |
+ Category |
*"year"* |
*"years"* |
+ ~date~ |
*"month"* |
*"months"* |
+ ~date~ |
*"week"* |
*"weeks"* |
+ ~date~ |
*"day"* |
*"days"* |
+ ~date~ |
*"hour"* |
*"hours"* |
+ ~time~ |
*"minute"* |
*"minutes"* |
+ ~time~ |
*"second"* |
*"seconds"* |
+ ~time~ |
*"millisecond"* |
*"milliseconds"* |
+ ~time~ |
*"microsecond"* |
*"microseconds"* |
+ ~time~ |
*"nanosecond"* |
*"nanoseconds"* |
+ ~time~ |
@@ -519,46 +503,17 @@ ToRelativeTemporalObject ( _options_ )
-
- ValidateTemporalUnitRange ( _largestUnit_, _smallestUnit_ )
-
- 1. If _smallestUnit_ is *"year"* and _largestUnit_ is not *"year"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"month"* and _largestUnit_ is not *"year"* or *"month"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"week"* and _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"day"* and _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"hour"* and _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, *"day"*, or *"hour"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"minute"* and _largestUnit_ is *"second"*, *"millisecond"*, *"microsecond"*, or *"nanosecond"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"second"* and _largestUnit_ is *"millisecond"*, *"microsecond"*, or *"nanosecond"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"millisecond"* and _largestUnit_ is *"microsecond"* or *"nanosecond"*, then
- 1. Throw a *RangeError* exception.
- 1. If _smallestUnit_ is *"microsecond"* and _largestUnit_ is *"nanosecond"*, then
- 1. Throw a *RangeError* exception.
-
-
-
LargerOfTwoTemporalUnits ( _u1_, _u2_ )
- The abstract operation LargerOfTwoTemporalUnits, given two strings representing units of time, returns the string representing the larger of the two units.
+ The abstract operation LargerOfTwoTemporalUnits, given two strings representing Temporal units, returns the string representing the larger of the two units.
- 1. If either _u1_ or _u2_ is *"year"*, return *"year"*.
- 1. If either _u1_ or _u2_ is *"month"*, return *"month"*.
- 1. If either _u1_ or _u2_ is *"week"*, return *"week"*.
- 1. If either _u1_ or _u2_ is *"day"*, return *"day"*.
- 1. If either _u1_ or _u2_ is *"hour"*, return *"hour"*.
- 1. If either _u1_ or _u2_ is *"minute"*, return *"minute"*.
- 1. If either _u1_ or _u2_ is *"second"*, return *"second"*.
- 1. If either _u1_ or _u2_ is *"millisecond"*, return *"millisecond"*.
- 1. If either _u1_ or _u2_ is *"microsecond"*, return *"microsecond"*.
- 1. Return *"nanosecond"*.
+ 1. Assert: Both _u1_ and _u2_ are listed in the Singular column of .
+ 1. For each row of , except the header row, in table order, do
+ 1. Let _unit_ be the value in the Singular column of the row.
+ 1. If SameValue(_u1_, _unit_) is *true*, return _unit_.
+ 1. If SameValue(_u2_, _unit_) is *true*, return _unit_.
diff --git a/spec/calendar.html b/spec/calendar.html
index 36c2b17099..dcaaf872be 100644
--- a/spec/calendar.html
+++ b/spec/calendar.html
@@ -847,7 +847,8 @@ Temporal.Calendar.prototype.dateUntil ( _one_, _two_ [ , _options_ ] )
1. Set _one_ to ? ToTemporalDate(_one_).
1. Set _two_ to ? ToTemporalDate(_two_).
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"* », *"auto"*, *"day"*).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~date~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to *"day"*.
1. Let _result_ be DifferenceISODate(_one_.[[ISOYear]], _one_.[[ISOMonth]], _one_.[[ISODay]], _two_.[[ISOYear]], _two_.[[ISOMonth]], _two_.[[ISODay]], _largestUnit_).
1. Return ! CreateTemporalDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], 0, 0, 0, 0, 0, 0).
diff --git a/spec/duration.html b/spec/duration.html
index e259935a3e..a955979d3b 100644
--- a/spec/duration.html
+++ b/spec/duration.html
@@ -413,13 +413,13 @@ Temporal.Duration.prototype.round ( _roundTo_ )
1. Set _roundTo_ to ? GetOptionsObject(_roundTo_).
1. Let _smallestUnitPresent_ be *true*.
1. Let _largestUnitPresent_ be *true*.
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « », *undefined*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~datetime~, *undefined*).
1. If _smallestUnit_ is *undefined*, then
1. Set _smallestUnitPresent_ to *false*.
1. Set _smallestUnit_ to *"nanosecond"*.
1. Let _defaultLargestUnit_ be ! DefaultTemporalLargestUnit(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]]).
1. Set _defaultLargestUnit_ to ! LargerOfTwoTemporalUnits(_defaultLargestUnit_, _smallestUnit_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_roundTo_, « », *undefined*).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_roundTo_, *"largestUnit"*, ~datetime~, *undefined*, « *"auto"* »).
1. If _largestUnit_ is *undefined*, then
1. Set _largestUnitPresent_ to *false*.
1. Set _largestUnit_ to _defaultLargestUnit_.
@@ -427,7 +427,7 @@ Temporal.Duration.prototype.round ( _roundTo_ )
1. Set _largestUnit_ to _defaultLargestUnit_.
1. If _smallestUnitPresent_ is *false* and _largestUnitPresent_ is *false*, then
1. Throw a *RangeError* exception.
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*).
1. Let _maximum_ be ! MaximumTemporalDurationRoundingIncrement(_smallestUnit_).
1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_roundTo_, _maximum_, *false*).
@@ -459,7 +459,7 @@ Temporal.Duration.prototype.total ( _totalOf_ )
1. Else,
1. Set _totalOf_ to ? GetOptionsObject(_totalOf_).
1. Let _relativeTo_ be ? ToRelativeTemporalObject(_totalOf_).
- 1. Let _unit_ be ? ToTemporalDurationTotalUnit(_totalOf_).
+ 1. Let _unit_ be ? GetTemporalUnit(_totalOf_, *"unit"*, ~datetime~, ~required~).
1. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_).
1. Let _intermediate_ be *undefined*.
1. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
diff --git a/spec/instant.html b/spec/instant.html
index ca2ec71ed3..82e237823c 100644
--- a/spec/instant.html
+++ b/spec/instant.html
@@ -286,8 +286,7 @@ Temporal.Instant.prototype.round ( _roundTo_ )
1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"smallestUnit"*, _paramString_).
1. Else,
1. Set _roundTo_ to ? GetOptionsObject(_roundTo_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*).
- 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception.
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~time~, ~required~).
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*).
1. If _smallestUnit_ is *"hour"*, then
1. Let _maximum_ be HoursPerDay.
@@ -655,10 +654,11 @@
1. If _operation_ is ~since~, let _sign_ be -1. Otherwise, let _sign_ be 1.
1. Set _other_ to ? ToTemporalInstant(_other_).
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *"nanosecond"*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~time~, *"nanosecond"*).
1. Let _defaultLargestUnit_ be ! LargerOfTwoTemporalUnits(*"second"*, _smallestUnit_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *"auto"*, _defaultLargestUnit_).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~time~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to _defaultLargestUnit_.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).
diff --git a/spec/intl.html b/spec/intl.html
index f807eea935..ccbe732363 100644
--- a/spec/intl.html
+++ b/spec/intl.html
@@ -1726,7 +1726,8 @@ Temporal.Calendar.prototype.dateUntil ( _one_, _two_ [ , _options_ ] )
1. Set _one_ to ? ToTemporalDate(_one_).
1. Set _two_ to ? ToTemporalDate(_two_).
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"* », *"auto"*, *"day"*).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~date~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to *"day"*.
1. If _calendar_.[[Identifier]] is *"iso8601"*, then
1. Let _result_ be DifferenceISODate(_one_.[[ISOYear]], _one_.[[ISOMonth]], _one_.[[ISODay]], _two_.[[ISOYear]], _two_.[[ISOMonth]], _two_.[[ISODay]], _largestUnit_).
1. Else,
diff --git a/spec/plaindate.html b/spec/plaindate.html
index 188f696585..05e7d90efe 100644
--- a/spec/plaindate.html
+++ b/spec/plaindate.html
@@ -944,11 +944,11 @@
1. Set _other_ to ? ToTemporalDate(_other_).
1. If ? CalendarEquals(_temporalDate_.[[Calendar]], _other_.[[Calendar]]) is *false*, throw a *RangeError* exception.
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _disallowedUnits_ be « *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"* ».
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, _disallowedUnits_, *"day"*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~date~, *"day"*).
1. Let _defaultLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _smallestUnit_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, _disallowedUnits_, *"auto"*, _defaultLargestUnit_).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~date~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to _defaultLargestUnit_.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).
diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html
index 5a06ead181..ed3e6139b6 100644
--- a/spec/plaindatetime.html
+++ b/spec/plaindatetime.html
@@ -512,8 +512,7 @@ Temporal.PlainDateTime.prototype.round ( _roundTo_ )
1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"smallestUnit"*, _paramString_).
1. Else,
1. Set _roundTo_ to ? GetOptionsObject(_roundTo_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"* », *undefined*).
- 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception.
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~time~, ~required~, « *"day"* »).
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*).
1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_roundTo_, _smallestUnit_).
1. Let _result_ be ! RoundISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _roundingIncrement_, _smallestUnit_, _roundingMode_).
@@ -1136,10 +1135,11 @@
1. Set _other_ to ? ToTemporalDateTime(_other_).
1. If ? CalendarEquals(_dateTime_.[[Calendar]], _other_.[[Calendar]]) is *false*, throw a *RangeError* exception.
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « », *"nanosecond"*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~datetime~, *"nanosecond"*).
1. Let _defaultLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _smallestUnit_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « », *"auto"*, _defaultLargestUnit_).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~datetime~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to _defaultLargestUnit_.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).
diff --git a/spec/plaintime.html b/spec/plaintime.html
index 46dfeaaa5c..08460bf09d 100644
--- a/spec/plaintime.html
+++ b/spec/plaintime.html
@@ -314,8 +314,7 @@ Temporal.PlainTime.prototype.round ( _roundTo_ )
1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"smallestUnit"*, _paramString_).
1. Else,
1. Set _roundTo_ to ? GetOptionsObject(_roundTo_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*).
- 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception.
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~time~, ~required~).
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*).
1. If _smallestUnit_ is *"hour"*, then
1. Let _maximum_ be 24.
@@ -951,9 +950,10 @@
1. If _operation_ is ~since~, let _sign_ be -1. Otherwise, let _sign_ be 1.
1. Set _other_ to ? ToTemporalTime(_other_).
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *"nanosecond"*).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *"auto"*, *"hour"*).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~time~, *"nanosecond"*).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~time~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to *"hour"*.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).
diff --git a/spec/plainyearmonth.html b/spec/plainyearmonth.html
index 3d6c5bf9b6..2c72d77019 100644
--- a/spec/plainyearmonth.html
+++ b/spec/plainyearmonth.html
@@ -628,10 +628,12 @@
1. Let _calendar_ be _yearMonth_.[[Calendar]].
1. If ? CalendarEquals(_calendar_, _other_.[[Calendar]]) is *false*, throw a *RangeError* exception.
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _disallowedUnits_ be « *"week"*, *"day"*, *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"* ».
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, _disallowedUnits_, *"month"*).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, _disallowedUnits_, *"auto"*, *"year"*).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~date~, *"month"*).
+ 1. If _smallestUnit_ is *"week"* or *"day"*, throw a *RangeError* exception.
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~date~, *"auto"*).
+ 1. If _largestUnit_ is *"week"* or *"day"*, throw a *RangeError* exception.
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to *"year"*.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).
diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html
index 68fb549ec0..5ac0bf37c9 100644
--- a/spec/zoneddatetime.html
+++ b/spec/zoneddatetime.html
@@ -727,8 +727,7 @@ Temporal.ZonedDateTime.prototype.round ( _roundTo_ )
1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"smallestUnit"*, _paramString_).
1. Else,
1. Set _roundTo_ to ? GetOptionsObject(_roundTo_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"* », *undefined*).
- 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception.
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~time~, ~required~, « *"day"* »).
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*).
1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_roundTo_, _smallestUnit_).
1. Let _timeZone_ be _zonedDateTime_.[[TimeZone]].
@@ -1363,10 +1362,11 @@
1. If ? CalendarEquals(_zonedDateTime_.[[Calendar]], _other_.[[Calendar]]) is *false*, then
1. Throw a *RangeError* exception.
1. Set _options_ to ? GetOptionsObject(_options_).
- 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « », *"nanosecond"*).
+ 1. Let _smallestUnit_ be ? GetTemporalUnit(_options_, *"smallestUnit"*, ~datetime~, *"nanosecond"*).
1. Let _defaultLargestUnit_ be ! LargerOfTwoTemporalUnits(*"hour"*, _smallestUnit_).
- 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « », *"auto"*, _defaultLargestUnit_).
- 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_).
+ 1. Let _largestUnit_ be ? GetTemporalUnit(_options_, *"largestUnit"*, ~datetime~, *"auto"*).
+ 1. If _largestUnit_ is *"auto"*, set _largestUnit_ to _defaultLargestUnit_.
+ 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception.
1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"trunc"*).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to ! NegateTemporalRoundingMode(_roundingMode_).