Skip to content

Commit 31d3aa8

Browse files
ptomatojustingrant
authored andcommitted
Normative: Alphabetize order of options bag property accesses
For every Temporal API that takes an options bag, ensure that the options bag's properties are accessed in alphabetical order. Validation of a property's value should normally follow the property access. An exception to this is validation that requires the values of more than one property. This should be done after both property values are obtained. See: #2254 UPSTREAM_COMMIT=2cc46eb9bfa798bfe2973e4a73437d9c2792077b
1 parent a1aea41 commit 31d3aa8

7 files changed

+50
-44
lines changed

lib/duration.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,19 @@ export class Duration implements Temporal.Duration {
232232
typeof roundToParam === 'string'
233233
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
234234
: ES.GetOptionsObject(roundToParam);
235+
236+
let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
237+
let relativeTo = ES.ToRelativeTemporalObject(roundTo);
238+
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
239+
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
235240
let smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'datetime', undefined);
241+
236242
let smallestUnitPresent = true;
237243
if (!smallestUnit) {
238244
smallestUnitPresent = false;
239245
smallestUnit = 'nanosecond';
240246
}
241247
defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit);
242-
let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
243248
let largestUnitPresent = true;
244249
if (!largestUnit) {
245250
largestUnitPresent = false;
@@ -252,7 +257,6 @@ export class Duration implements Temporal.Duration {
252257
if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
253258
throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
254259
}
255-
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
256260

257261
const maximumIncrements = {
258262
hour: 24,
@@ -262,10 +266,8 @@ export class Duration implements Temporal.Duration {
262266
microsecond: 1000,
263267
nanosecond: 1000
264268
} as { [k in Temporal.DateTimeUnit]?: number };
265-
let roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
266269
const maximum = maximumIncrements[smallestUnit];
267270
if (maximum !== undefined) ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false);
268-
let relativeTo = ES.ToRelativeTemporalObject(roundTo);
269271

270272
({ years, months, weeks, days } = ES.UnbalanceDurationRelative(
271273
years,
@@ -395,6 +397,7 @@ export class Duration implements Temporal.Duration {
395397
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
396398
const options = ES.GetOptionsObject(optionsParam);
397399
const digits = ES.ToFractionalSecondDigits(options);
400+
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
398401
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
399402
if (smallestUnit === 'hour' || smallestUnit === 'minute') {
400403
throw new RangeError('smallestUnit must be a time unit other than "hours" or "minutes"');
@@ -404,7 +407,6 @@ export class Duration implements Temporal.Duration {
404407
precision,
405408
'Precision cannot be "minute" because of RangeError above'
406409
);
407-
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
408410
return ES.TemporalDurationToString(this, precision, { unit, increment, roundingMode });
409411
}
410412
toJSON(): Return['toJSON'] {

lib/ecmascript.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,8 @@ export function ToTemporalZonedDateTime(
17901790
timeZone,
17911791
offset: string | undefined,
17921792
calendar: string | Temporal.CalendarProtocol | undefined;
1793+
let disambiguation: NonNullable<Temporal.ToInstantOptions['disambiguation']>;
1794+
let offsetOpt: NonNullable<Temporal.OffsetDisambiguationOptions['offset']>;
17931795
let matchMinute = false;
17941796
let offsetBehaviour: OffsetBehaviour = 'option';
17951797
if (IsObject(item)) {
@@ -1815,13 +1817,14 @@ export function ToTemporalZonedDateTime(
18151817
if (offset === undefined) {
18161818
offsetBehaviour = 'wall';
18171819
}
1820+
disambiguation = ToTemporalDisambiguation(options);
1821+
offsetOpt = ToTemporalOffset(options, 'reject');
18181822
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = InterpretTemporalDateTimeFields(
18191823
calendar,
18201824
fields,
18211825
options
18221826
));
18231827
} else {
1824-
ToTemporalOverflow(options); // validate and ignore
18251828
let ianaName, z;
18261829
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, ianaName, offset, z, calendar } =
18271830
ParseTemporalZonedDateTimeString(ToString(item)));
@@ -1836,13 +1839,14 @@ export function ToTemporalZonedDateTime(
18361839
if (!calendar) calendar = GetISO8601Calendar();
18371840
calendar = ToTemporalCalendar(calendar);
18381841
matchMinute = true; // ISO strings may specify offset with less precision
1842+
disambiguation = ToTemporalDisambiguation(options);
1843+
offsetOpt = ToTemporalOffset(options, 'reject');
1844+
ToTemporalOverflow(options); // validate and ignore
18391845
}
18401846
let offsetNs = 0;
18411847
// The code above guarantees that if offsetBehaviour === 'option', then
18421848
// `offset` is not undefined.
18431849
if (offsetBehaviour === 'option') offsetNs = ParseTimeZoneOffsetString(offset as string);
1844-
const disambiguation = ToTemporalDisambiguation(options);
1845-
const offsetOpt = ToTemporalOffset(options, 'reject');
18461850
const epochNanoseconds = InterpretISODateTimeOffset(
18471851
year,
18481852
month,
@@ -4447,21 +4451,26 @@ function GetDifferenceSettings<T extends Temporal.DateTimeUnit>(
44474451
return allowed;
44484452
}, [] as (Temporal.DateTimeUnit | Temporal.PluralUnit<Temporal.DateTimeUnit>)[]);
44494453

4454+
let largestUnit = GetTemporalUnit(options, 'largestUnit', group, 'auto');
4455+
if (disallowed.includes(largestUnit)) {
4456+
throw new RangeError(`largestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${largestUnit}`);
4457+
}
4458+
4459+
const roundingIncrement = ToTemporalRoundingIncrement(options);
4460+
4461+
let roundingMode = ToTemporalRoundingMode(options, 'trunc');
4462+
if (op === 'since') roundingMode = NegateTemporalRoundingMode(roundingMode);
4463+
44504464
const smallestUnit = GetTemporalUnit(options, 'smallestUnit', group, fallbackSmallest);
44514465
if (disallowed.includes(smallestUnit)) {
44524466
throw new RangeError(`smallestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${smallestUnit}`);
44534467
}
4468+
44544469
const defaultLargestUnit = LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit);
4455-
let largestUnit = GetTemporalUnit(options, 'largestUnit', group, 'auto');
4456-
if (disallowed.includes(largestUnit)) {
4457-
throw new RangeError(`largestUnit must be one of ${ALLOWED_UNITS.join(', ')}, not ${largestUnit}`);
4458-
}
44594470
if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
44604471
if (LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
44614472
throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
44624473
}
4463-
let roundingMode = ToTemporalRoundingMode(options, 'trunc');
4464-
if (op === 'since') roundingMode = NegateTemporalRoundingMode(roundingMode);
44654474
const MAX_DIFFERENCE_INCREMENTS: { [k in Temporal.DateTimeUnit]?: number } = {
44664475
hour: 24,
44674476
minute: 60,
@@ -4470,7 +4479,6 @@ function GetDifferenceSettings<T extends Temporal.DateTimeUnit>(
44704479
microsecond: 1000,
44714480
nanosecond: 1000
44724481
};
4473-
const roundingIncrement = ToTemporalRoundingIncrement(options);
44744482
const maximum = MAX_DIFFERENCE_INCREMENTS[smallestUnit];
44754483
if (maximum !== undefined) ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false);
44764484

lib/instant.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ export class Instant implements Temporal.Instant {
7676
typeof roundToParam === 'string'
7777
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
7878
: ES.GetOptionsObject(roundToParam);
79-
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
79+
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
8080
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
81+
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
8182
const maximumIncrements = {
8283
hour: 24,
8384
minute: 1440,
@@ -86,7 +87,6 @@ export class Instant implements Temporal.Instant {
8687
microsecond: 86400e6,
8788
nanosecond: 86400e9
8889
};
89-
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
9090
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximumIncrements[smallestUnit], true);
9191
const ns = GetSlot(this, EPOCHNANOSECONDS);
9292
const roundedNs = ES.RoundInstant(ns, roundingIncrement, smallestUnit, roundingMode);
@@ -102,13 +102,13 @@ export class Instant implements Temporal.Instant {
102102
toString(optionsParam: Params['toString'][0] = undefined): string {
103103
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
104104
const options = ES.GetOptionsObject(optionsParam);
105-
let timeZone = options.timeZone;
106-
if (timeZone !== undefined) timeZone = ES.ToTemporalTimeZone(timeZone);
107105
const digits = ES.ToFractionalSecondDigits(options);
106+
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
108107
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
109108
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
109+
let timeZone = options.timeZone;
110+
if (timeZone !== undefined) timeZone = ES.ToTemporalTimeZone(timeZone);
110111
const { precision, unit, increment } = ES.ToSecondsStringPrecision(smallestUnit, digits);
111-
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
112112
const ns = GetSlot(this, EPOCHNANOSECONDS);
113113
const roundedNs = ES.RoundInstant(ns, increment, unit, roundingMode);
114114
const roundedInstant = new Instant(roundedNs);

lib/plaindatetime.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,9 @@ export class PlainDateTime implements Temporal.PlainDateTime {
299299
typeof roundToParam === 'string'
300300
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
301301
: ES.GetOptionsObject(roundToParam);
302-
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
302+
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
303303
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
304+
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
304305
const maximumIncrements = {
305306
day: 1,
306307
hour: 24,
@@ -310,7 +311,6 @@ export class PlainDateTime implements Temporal.PlainDateTime {
310311
microsecond: 1000,
311312
nanosecond: 1000
312313
};
313-
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
314314
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximumIncrements[smallestUnit], false);
315315

316316
let year = GetSlot(this, ISO_YEAR);
@@ -373,12 +373,12 @@ export class PlainDateTime implements Temporal.PlainDateTime {
373373
toString(optionsParam: Params['toString'][0] = undefined): string {
374374
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
375375
const options = ES.GetOptionsObject(optionsParam);
376+
const showCalendar = ES.ToCalendarNameOption(options);
376377
const digits = ES.ToFractionalSecondDigits(options);
378+
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
377379
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
378380
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
379381
const { precision, unit, increment } = ES.ToSecondsStringPrecision(smallestUnit, digits);
380-
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
381-
const showCalendar = ES.ToCalendarNameOption(options);
382382
return ES.TemporalDateTimeToString(this, precision, showCalendar, { unit, increment, roundingMode });
383383
}
384384
toJSON(): Return['toJSON'] {

lib/plaintime.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,9 @@ export class PlainTime implements Temporal.PlainTime {
176176
typeof roundToParam === 'string'
177177
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
178178
: ES.GetOptionsObject(roundToParam);
179-
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
179+
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
180180
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
181+
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
181182
const MAX_INCREMENTS = {
182183
hour: 24,
183184
minute: 60,
@@ -186,7 +187,6 @@ export class PlainTime implements Temporal.PlainTime {
186187
microsecond: 1000,
187188
nanosecond: 1000
188189
};
189-
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
190190
ES.ValidateTemporalRoundingIncrement(roundingIncrement, MAX_INCREMENTS[smallestUnit], false);
191191

192192
let hour = GetSlot(this, ISO_HOUR);
@@ -224,10 +224,10 @@ export class PlainTime implements Temporal.PlainTime {
224224
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
225225
const options = ES.GetOptionsObject(optionsParam);
226226
const digits = ES.ToFractionalSecondDigits(options);
227+
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
227228
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
228229
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
229230
const { precision, unit, increment } = ES.ToSecondsStringPrecision(smallestUnit, digits);
230-
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
231231
return TemporalTimeToString(this, precision, { unit, increment, roundingMode });
232232
}
233233
toJSON(): Return['toJSON'] {

lib/zoneddatetime.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,17 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
201201
uncheckedAssertWritableArray(fieldNamesWithOffset);
202202
const props = ES.PrepareTemporalFields(temporalZonedDateTimeLike, fieldNamesWithOffset, 'partial');
203203

204-
const options = ES.GetOptionsObject(optionsParam);
205-
const disambiguation = ES.ToTemporalDisambiguation(options);
206-
const offset = ES.ToTemporalOffset(options, 'prefer');
207-
208204
const timeZone = GetSlot(this, TIME_ZONE);
209205
const fieldNamesWithTimeZoneAndOffset = ES.ArrayPush(fieldNamesWithOffset, 'timeZone');
210206
uncheckedAssertWritableArray(fieldNamesWithTimeZoneAndOffset);
211207
let fields = ES.PrepareTemporalFields(this, fieldNamesWithTimeZoneAndOffset, ['timeZone', 'offset']);
212208
fields = ES.CalendarMergeFields(calendar, fields, props);
213209
fields = ES.PrepareTemporalFields(fields, fieldNamesWithTimeZoneAndOffset, ['timeZone', 'offset']);
210+
211+
const options = ES.GetOptionsObject(optionsParam);
212+
const disambiguation = ES.ToTemporalDisambiguation(options);
213+
const offset = ES.ToTemporalOffset(options, 'prefer');
214+
214215
let { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } =
215216
ES.InterpretTemporalDateTimeFields(calendar, fields, options);
216217
const offsetNs = ES.ParseTimeZoneOffsetString(fields.offset);
@@ -340,8 +341,9 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
340341
typeof roundToParam === 'string'
341342
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
342343
: ES.GetOptionsObject(roundToParam);
343-
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
344+
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
344345
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
346+
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
345347
const maximumIncrements = {
346348
day: 1,
347349
hour: 24,
@@ -351,7 +353,6 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
351353
microsecond: 1000,
352354
nanosecond: 1000
353355
};
354-
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
355356
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximumIncrements[smallestUnit], false);
356357

357358
// first, round the underlying DateTime fields
@@ -432,14 +433,14 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
432433
toString(optionsParam: Params['toString'][0] = undefined): string {
433434
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
434435
const options = ES.GetOptionsObject(optionsParam);
436+
const showCalendar = ES.ToCalendarNameOption(options);
435437
const digits = ES.ToFractionalSecondDigits(options);
438+
const showOffset = ES.ToShowOffsetOption(options);
439+
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
436440
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
437441
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
438-
const { precision, unit, increment } = ES.ToSecondsStringPrecision(smallestUnit, digits);
439-
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
440-
const showCalendar = ES.ToCalendarNameOption(options);
441442
const showTimeZone = ES.ToTimeZoneNameOption(options);
442-
const showOffset = ES.ToShowOffsetOption(options);
443+
const { precision, unit, increment } = ES.ToSecondsStringPrecision(smallestUnit, digits);
443444
return ES.TemporalZonedDateTimeToString(this, precision, showCalendar, showTimeZone, showOffset, {
444445
unit,
445446
increment,
@@ -534,9 +535,9 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
534535
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
535536
const options = ES.GetOptionsObject(optionsParam);
536537
if (ES.IsTemporalZonedDateTime(item)) {
537-
ES.ToTemporalOverflow(options); // validate and ignore
538-
ES.ToTemporalDisambiguation(options);
538+
ES.ToTemporalDisambiguation(options); // validate and ignore
539539
ES.ToTemporalOffset(options, 'reject');
540+
ES.ToTemporalOverflow(options);
540541
return ES.CreateTemporalZonedDateTime(
541542
GetSlot(item, EPOCHNANOSECONDS),
542543
GetSlot(item, TIME_ZONE),

0 commit comments

Comments
 (0)