Skip to content

Normative: Validate unit-valued options after all options are read #3130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion polyfill/lib/duration.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ export class Duration {
}

let largestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'largestUnit');
ES.ValidateTemporalUnitValue(largestUnit, 'datetime', ['auto']);
let { plainRelativeTo, zonedRelativeTo } = ES.GetTemporalRelativeToOption(roundTo);
const roundingIncrement = ES.GetRoundingIncrementOption(roundTo);
const roundingMode = ES.GetRoundingModeOption(roundTo, 'halfExpand');
let smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit');
ES.ValidateTemporalUnitValue(largestUnit, 'datetime', ['auto']);
ES.ValidateTemporalUnitValue(smallestUnit, 'datetime');

let smallestUnitPresent = true;
Expand Down
8 changes: 4 additions & 4 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3636,6 +3636,10 @@ export function GetDifferenceSettings(op, options, group, disallowed, fallbackSm
]);

let largestUnit = GetTemporalUnitValuedOption(options, 'largestUnit');
const roundingIncrement = GetRoundingIncrementOption(options);
let roundingMode = GetRoundingModeOption(options, 'trunc');
let smallestUnit = GetTemporalUnitValuedOption(options, 'smallestUnit');

ValidateTemporalUnitValue(largestUnit, group, ['auto']);
if (!largestUnit) largestUnit = 'auto';
if (Call(ArrayPrototypeIncludes, disallowed, [largestUnit])) {
Expand All @@ -3644,12 +3648,8 @@ export function GetDifferenceSettings(op, options, group, disallowed, fallbackSm
);
}

const roundingIncrement = GetRoundingIncrementOption(options);

let roundingMode = GetRoundingModeOption(options, 'trunc');
if (op === 'since') roundingMode = NegateRoundingMode(roundingMode);

let smallestUnit = GetTemporalUnitValuedOption(options, 'smallestUnit');
ValidateTemporalUnitValue(smallestUnit, group);
if (!smallestUnit) smallestUnit = fallbackSmallest;
if (Call(ArrayPrototypeIncludes, disallowed, [smallestUnit])) {
Expand Down
2 changes: 1 addition & 1 deletion polyfill/lib/instant.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ export class Instant {
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
let timeZone = resolvedOptions.timeZone;
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
if (smallestUnit === 'hour') throw new RangeErrorCtor('smallestUnit must be a time unit other than "hour"');
let timeZone = resolvedOptions.timeZone;
if (timeZone !== undefined) timeZone = ES.ToTemporalTimeZoneIdentifier(timeZone);
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
const ns = GetSlot(this, EPOCHNANOSECONDS);
Expand Down
2 changes: 1 addition & 1 deletion polyfill/lib/zoneddatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,9 @@ export class ZonedDateTime {
const showOffset = ES.GetTemporalShowOffsetOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
const showTimeZone = ES.GetTemporalShowTimeZoneNameOption(resolvedOptions);
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
if (smallestUnit === 'hour') throw new RangeErrorCtor('smallestUnit must be a time unit other than "hour"');
const showTimeZone = ES.GetTemporalShowTimeZoneNameOption(resolvedOptions);
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
return ES.TemporalZonedDateTimeToString(this, precision, showCalendar, showTimeZone, showOffset, {
unit,
Expand Down
6 changes: 3 additions & 3 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -1866,15 +1866,15 @@ <h1>
<emu-alg>
1. NOTE: The following steps read options and perform independent validation in alphabetical order.
1. Let _largestUnit_ be ? GetTemporalUnitValuedOption(_options_, *"largestUnit"*, ~unset~).
1. Let _roundingIncrement_ be ? GetRoundingIncrementOption(_options_).
1. Let _roundingMode_ be ? GetRoundingModeOption(_options_, ~trunc~).
1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_options_, *"smallestUnit"*, ~unset~).
1. Perform ? ValidateTemporalUnitValue(_largestUnit_, _unitGroup_, « ~auto~ »).
1. If _largestUnit_ is ~unset~, then
1. Set _largestUnit_ to ~auto~.
1. If _disallowedUnits_ contains _largestUnit_, throw a *RangeError* exception.
1. Let _roundingIncrement_ be ? GetRoundingIncrementOption(_options_).
1. Let _roundingMode_ be ? GetRoundingModeOption(_options_, ~trunc~).
1. If _operation_ is ~since~, then
1. Set _roundingMode_ to NegateRoundingMode(_roundingMode_).
1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_options_, *"smallestUnit"*, ~unset~).
1. Perform ? ValidateTemporalUnitValue(_smallestUnit_, _unitGroup_).
1. If _smallestUnit_ is ~unset~, then
1. Set _smallestUnit_ to _fallbackSmallestUnit_.
Expand Down
2 changes: 1 addition & 1 deletion spec/duration.html
Original file line number Diff line number Diff line change
Expand Up @@ -403,13 +403,13 @@ <h1>Temporal.Duration.prototype.round ( _roundTo_ )</h1>
1. Let _largestUnitPresent_ be *true*.
1. NOTE: The following steps read options and perform independent validation in alphabetical order (GetTemporalRelativeToOption reads *"relativeTo"*, GetRoundingIncrementOption reads *"roundingIncrement"* and GetRoundingModeOption reads *"roundingMode"*).
1. Let _largestUnit_ be ? GetTemporalUnitValuedOption(_roundTo_, *"largestUnit"*, ~unset~).
1. Perform ? ValidateTemporalUnitValue(_largestUnit_, ~datetime~, « ~auto~ »).
1. Let _relativeToRecord_ be ? GetTemporalRelativeToOption(_roundTo_).
1. Let _zonedRelativeTo_ be _relativeToRecord_.[[ZonedRelativeTo]].
1. Let _plainRelativeTo_ be _relativeToRecord_.[[PlainRelativeTo]].
1. Let _roundingIncrement_ be ? GetRoundingIncrementOption(_roundTo_).
1. Let _roundingMode_ be ? GetRoundingModeOption(_roundTo_, ~half-expand~).
1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_roundTo_, *"smallestUnit"*, ~unset~).
1. Perform ? ValidateTemporalUnitValue(_largestUnit_, ~datetime~, « ~auto~ »).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it actually the case that this is a no-op? GetTemporalUnitValuedOption can return any unit, AUTO, or UNSET. And this particular invocation of ValidateTemporalUnitValue permits any unit, AUTO, or UNSET.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ~datetime~ is the superset of all units, then your observation seems correct

1. Perform ? ValidateTemporalUnitValue(_smallestUnit_, ~datetime~).
1. If _smallestUnit_ is ~unset~, then
1. Set _smallestUnitPresent_ to *false*.
Expand Down
2 changes: 1 addition & 1 deletion spec/instant.html
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,9 @@ <h1>Temporal.Instant.prototype.toString ( [ _options_ ] )</h1>
1. Let _digits_ be ? GetTemporalFractionalSecondDigitsOption(_resolvedOptions_).
1. Let _roundingMode_ be ? GetRoundingModeOption(_resolvedOptions_, ~trunc~).
1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_resolvedOptions_, *"smallestUnit"*, ~unset~).
1. Let _timeZone_ be ? Get(_resolvedOptions_, *"timeZone"*).
1. Perform ? ValidateTemporalUnitValue(_smallestUnit_, ~time~).
1. If _smallestUnit_ is ~hour~, throw a *RangeError* exception.
1. Let _timeZone_ be ? Get(_resolvedOptions_, *"timeZone"*).
1. If _timeZone_ is not *undefined*, then
1. Set _timeZone_ to ? ToTemporalTimeZoneIdentifier(_timeZone_).
1. Let _precision_ be ToSecondsStringPrecisionRecord(_smallestUnit_, _digits_).
Expand Down
2 changes: 1 addition & 1 deletion spec/zoneddatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,9 @@ <h1>Temporal.ZonedDateTime.prototype.toString ( [ _options_ ] )</h1>
1. Let _showOffset_ be ? GetTemporalShowOffsetOption(_resolvedOptions_).
1. Let _roundingMode_ be ? GetRoundingModeOption(_resolvedOptions_, ~trunc~).
1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_resolvedOptions_, *"smallestUnit"*, ~unset~).
1. Let _showTimeZone_ be ? GetTemporalShowTimeZoneNameOption(_resolvedOptions_).
1. Perform ? ValidateTemporalUnitValue(_smallestUnit_, ~time~).
1. If _smallestUnit_ is ~hour~, throw a *RangeError* exception.
1. Let _showTimeZone_ be ? GetTemporalShowTimeZoneNameOption(_resolvedOptions_).
1. Let _precision_ be ToSecondsStringPrecisionRecord(_smallestUnit_, _digits_).
1. Return TemporalZonedDateTimeToString(_zonedDateTime_, _precision_.[[Precision]], _showCalendar_, _showTimeZone_, _showOffset_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_).
</emu-alg>
Expand Down