Skip to content
Merged
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
12 changes: 8 additions & 4 deletions polyfill/lib/duration.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,13 @@ export class Duration {
roundTo = ES.GetOptionsObject(roundTo);
}

let largestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
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', 'datetime', undefined);
let smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit');
ES.ValidateTemporalUnitValue(smallestUnit, 'datetime');

let smallestUnitPresent = true;
if (!smallestUnit) {
Expand Down Expand Up @@ -311,7 +313,8 @@ export class Duration {
totalOf = ES.GetOptionsObject(totalOf);
}
let { plainRelativeTo, zonedRelativeTo } = ES.GetTemporalRelativeToOption(totalOf);
const unit = ES.GetTemporalUnitValuedOption(totalOf, 'unit', 'datetime', ES.REQUIRED);
const unit = ES.GetTemporalUnitValuedOption(totalOf, 'unit', ES.REQUIRED);
ES.ValidateTemporalUnitValue(unit, 'datetime');

if (zonedRelativeTo) {
const duration = ES.ToInternalDurationRecord(this);
Expand Down Expand Up @@ -353,7 +356,8 @@ export class Duration {
const resolvedOptions = ES.GetOptionsObject(options);
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit', 'time', undefined);
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
if (smallestUnit === 'hour' || smallestUnit === 'minute') {
throw new RangeErrorCtor('smallestUnit must be a time unit other than "hours" or "minutes"');
}
Expand Down
71 changes: 35 additions & 36 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ const TEMPORAL_UNITS = [
];
const SINGULAR_FOR = new MapCtor(TEMPORAL_UNITS);
// Iterable destructuring is acceptable in this first-run code.
const PLURAL_FOR = new MapCtor(Call(ArrayPrototypeMap, TEMPORAL_UNITS, [([p, s]) => [s, p]]));
const UNITS_DESCENDING = Call(ArrayPrototypeMap, TEMPORAL_UNITS, [([, s]) => s]);
const NS_PER_TIME_UNIT = new MapCtor(
Call(ArrayPrototypeFlatMap, TEMPORAL_UNITS, [([, s, , l]) => (l ? [[s, l]] : [])])
Expand Down Expand Up @@ -918,38 +917,35 @@ export function ToSecondsStringPrecisionRecord(smallestUnit, precision) {

export const REQUIRED = SymbolCtor('~required~');

export function GetTemporalUnitValuedOption(options, key, unitGroup, requiredOrDefault, extraValues = []) {
const allowedSingular = [];
export function GetTemporalUnitValuedOption(options, key, requiredOrUndefined = undefined) {
const allowedStrings = [];
for (let index = 0; index < TEMPORAL_UNITS.length; index++) {
const unitInfo = TEMPORAL_UNITS[index];
const plural = unitInfo[0];
const singular = unitInfo[1];
const category = unitInfo[2];
if (unitGroup === 'datetime' || unitGroup === category) {
Call(ArrayPrototypePush, allowedSingular, [singular]);
}
}
Call(ArrayPrototypePush, allowedSingular, extraValues);
let defaultVal = requiredOrDefault;
if (defaultVal === REQUIRED) {
defaultVal = undefined;
} else if (defaultVal !== undefined) {
Call(ArrayPrototypePush, allowedSingular, [defaultVal]);
}
const allowedValues = [];
Call(ArrayPrototypePush, allowedValues, allowedSingular);
for (let index = 0; index < allowedSingular.length; index++) {
const singular = allowedSingular[index];
const plural = Call(MapPrototypeGet, PLURAL_FOR, [singular]);
if (plural !== undefined) Call(ArrayPrototypePush, allowedValues, [plural]);
}
let retval = GetOption(options, key, allowedValues, defaultVal);
if (retval === undefined && requiredOrDefault === REQUIRED) {
throw new RangeErrorCtor(`${key} is required`);
Call(ArrayPrototypePush, allowedStrings, [singular, plural]);
}
Call(ArrayPrototypePush, allowedStrings, ['auto']);
let retval = GetOption(options, key, allowedStrings, requiredOrUndefined);
if (retval === undefined) return undefined;
if (Call(MapPrototypeHas, SINGULAR_FOR, [retval])) retval = Call(MapPrototypeGet, SINGULAR_FOR, [retval]);
return retval;
}

export function ValidateTemporalUnitValue(value, unitGroup, extraValues = []) {
if (value === undefined) return;
if (Call(ArrayPrototypeIncludes, extraValues, [value])) return;
for (let index = 0; index < TEMPORAL_UNITS.length; index++) {
const unitInfo = TEMPORAL_UNITS[index];
const singular = unitInfo[0];
const plural = unitInfo[1];
const category = unitInfo[2];
if (value !== singular && value !== plural) continue;
if (unitGroup === 'datetime' || unitGroup === category) return;
}
throw new RangeErrorCtor(`${value} not allowed as a ${unitGroup} unit`);
}

export function GetTemporalRelativeToOption(options) {
// returns: {
// plainRelativeTo: Temporal.PlainDate | undefined
Expand Down Expand Up @@ -1484,7 +1480,7 @@ export function InterpretISODateTimeOffset(
export function ToTemporalZonedDateTime(item, options = undefined) {
let isoDate, time, timeZone, offset, calendar;
let matchMinute = false;
let offsetBehaviour = 'option';
let hasUTCDesignator = false;
let disambiguation, offsetOpt;
if (Type(item) === 'Object') {
if (IsTemporalZonedDateTime(item)) {
Expand All @@ -1507,9 +1503,6 @@ export function ToTemporalZonedDateTime(item, options = undefined) {
['timeZone']
);
({ offset, timeZone } = fields);
if (offset === undefined) {
offsetBehaviour = 'wall';
}
const resolvedOptions = GetOptionsObject(options);
disambiguation = GetTemporalDisambiguationOption(resolvedOptions);
offsetOpt = GetTemporalOffsetOption(resolvedOptions, 'reject');
Expand All @@ -1521,11 +1514,7 @@ export function ToTemporalZonedDateTime(item, options = undefined) {
RequireString(item)
));
timeZone = ToTemporalTimeZoneIdentifier(tzAnnotation);
if (z) {
offsetBehaviour = 'exact';
} else if (!offset) {
offsetBehaviour = 'wall';
}
if (z) hasUTCDesignator = true;
if (!calendar) calendar = 'iso8601';
calendar = CanonicalizeCalendar(calendar);
// Allow imprecise offset matching unless the provided offset is precise
Expand All @@ -1542,6 +1531,12 @@ export function ToTemporalZonedDateTime(item, options = undefined) {
GetTemporalOverflowOption(resolvedOptions); // validate and ignore
isoDate = { year, month, day };
}
let offsetBehaviour = 'option';
if (hasUTCDesignator) {
offsetBehaviour = 'exact';
} else if (!offset) {
offsetBehaviour = 'wall';
}
let offsetNs = 0;
if (offsetBehaviour === 'option') offsetNs = ParseDateTimeUTCOffset(offset);
const epochNanoseconds = InterpretISODateTimeOffset(
Expand Down Expand Up @@ -3640,7 +3635,9 @@ export function GetDifferenceSettings(op, options, group, disallowed, fallbackSm
[]
]);

let largestUnit = GetTemporalUnitValuedOption(options, 'largestUnit', group, 'auto');
let largestUnit = GetTemporalUnitValuedOption(options, 'largestUnit');
ValidateTemporalUnitValue(largestUnit, group, ['auto']);
if (!largestUnit) largestUnit = 'auto';
if (Call(ArrayPrototypeIncludes, disallowed, [largestUnit])) {
throw new RangeErrorCtor(
`largestUnit must be one of ${Call(ArrayPrototypeJoin, ALLOWED_UNITS, [', '])}, not ${largestUnit}`
Expand All @@ -3652,7 +3649,9 @@ export function GetDifferenceSettings(op, options, group, disallowed, fallbackSm
let roundingMode = GetRoundingModeOption(options, 'trunc');
if (op === 'since') roundingMode = NegateRoundingMode(roundingMode);

const smallestUnit = GetTemporalUnitValuedOption(options, 'smallestUnit', group, fallbackSmallest);
let smallestUnit = GetTemporalUnitValuedOption(options, 'smallestUnit');
ValidateTemporalUnitValue(smallestUnit, group);
if (!smallestUnit) smallestUnit = fallbackSmallest;
if (Call(ArrayPrototypeIncludes, disallowed, [smallestUnit])) {
throw new RangeErrorCtor(
`smallestUnit must be one of ${Call(ArrayPrototypeJoin, ALLOWED_UNITS, [', '])}, not ${smallestUnit}`
Expand Down
6 changes: 4 additions & 2 deletions polyfill/lib/instant.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ export class Instant {
}
const roundingIncrement = ES.GetRoundingIncrementOption(roundTo);
const roundingMode = ES.GetRoundingModeOption(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', ES.REQUIRED);
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
const maximumIncrements = {
hour: 24,
minute: 1440,
Expand All @@ -107,7 +108,8 @@ export class Instant {
const resolvedOptions = ES.GetOptionsObject(options);
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit', 'time', undefined);
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
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);
Expand Down
6 changes: 4 additions & 2 deletions polyfill/lib/plaindatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ export class PlainDateTime {
}
const roundingIncrement = ES.GetRoundingIncrementOption(roundTo);
const roundingMode = ES.GetRoundingModeOption(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', ES.REQUIRED);
ES.ValidateTemporalUnitValue(smallestUnit, 'time', ['day']);
const maximumIncrements = {
day: 1,
hour: 24,
Expand Down Expand Up @@ -246,7 +247,8 @@ export class PlainDateTime {
const showCalendar = ES.GetTemporalShowCalendarNameOption(resolvedOptions);
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit', 'time', undefined);
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
if (smallestUnit === 'hour') throw new RangeErrorCtor('smallestUnit must be a time unit other than "hour"');
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
const result = ES.RoundISODateTime(GetSlot(this, ISO_DATE_TIME), increment, unit, roundingMode);
Expand Down
6 changes: 4 additions & 2 deletions polyfill/lib/plaintime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ export class PlainTime {
}
const roundingIncrement = ES.GetRoundingIncrementOption(roundTo);
const roundingMode = ES.GetRoundingModeOption(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', ES.REQUIRED);
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
const MAX_INCREMENTS = {
hour: 24,
minute: 60,
Expand All @@ -130,7 +131,8 @@ export class PlainTime {
const resolvedOptions = ES.GetOptionsObject(options);
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit', 'time', undefined);
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
ES.ValidateTemporalUnitValue(smallestUnit, 'time');
if (smallestUnit === 'hour') throw new RangeErrorCtor('smallestUnit must be a time unit other than "hour"');
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
const time = ES.RoundTime(GetSlot(this, TIME), increment, unit, roundingMode);
Expand Down
6 changes: 4 additions & 2 deletions polyfill/lib/zoneddatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ export class ZonedDateTime {
}
const roundingIncrement = ES.GetRoundingIncrementOption(roundTo);
const roundingMode = ES.GetRoundingModeOption(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const smallestUnit = ES.GetTemporalUnitValuedOption(roundTo, 'smallestUnit', ES.REQUIRED);
ES.ValidateTemporalUnitValue(smallestUnit, 'time', ['day']);
const maximumIncrements = {
day: 1,
hour: 24,
Expand Down Expand Up @@ -347,7 +348,8 @@ export class ZonedDateTime {
const digits = ES.GetTemporalFractionalSecondDigitsOption(resolvedOptions);
const showOffset = ES.GetTemporalShowOffsetOption(resolvedOptions);
const roundingMode = ES.GetRoundingModeOption(resolvedOptions, 'trunc');
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit', 'time', undefined);
const smallestUnit = ES.GetTemporalUnitValuedOption(resolvedOptions, 'smallestUnit');
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);
Expand Down
12 changes: 3 additions & 9 deletions spec/calendar.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ <h1>Abstract Operations</h1>
<emu-clause id="sec-temporal-calendar-date-records">
<h1>Calendar Date Records</h1>
<p>
An <dfn variants="Calendar Date Records">Calendar Date Record</dfn> is a Record value used to represent a valid calendar date in a non-ISO 8601 calendar.
A <dfn variants="Calendar Date Records">Calendar Date Record</dfn> is a Record value used to represent a valid calendar date in a non-ISO 8601 calendar.
Calendar Date Records are produced by the abstract operation CalendarISOToDate.
</p>
<p>Calendar Date Records have the fields listed in <emu-xref href="#table-temporal-calendar-date-record-fields"></emu-xref>.</p>
Expand All @@ -73,7 +73,7 @@ <h1>Calendar Date Records</h1>
<td>
The ordinal position of the date's year within its era, or *undefined* for calendars that do not have eras.
<emu-note>
Era years are 1-indexed for many calendars, but not all (e.g., the eras of the Burmese calendar each start with a year 0). Years can also advance opposite the flow of time (as for BCE years in the Gregorian calendar).
Era years are 1-indexed for many calendars, but not all (e.g., the eras of the Burmese calendar, not currently available in CLDR, each start with a year 0). Years can also advance opposite the flow of time (as for BCE years in the Gregorian calendar).
</emu-note>
</td>
</tr>
Expand Down Expand Up @@ -101,7 +101,7 @@ <h1>Calendar Date Records</h1>
<td>
The month code of the date's month. The month code for a month that is not a leap month and whose 1-based ordinal position in a common year of the calendar (i.e., a year that is not a leap year) is _n_ should be the string-concatenation of *"M"* and ToZeroPaddedDecimalString(_n_, 2), and the month code for a month that is a leap month inserted after a month whose 1-based ordinal position in a common year of the calendar is _p_ should be the string-concatenation of *"M"*, ToZeroPaddedDecimalString(_p_, 2), and *"L"*.
<emu-note>
For example, in the Hebrew calendar, the month code of Adar (and Adar II, in leap years) is *"M06"* and the month code of Adar I (the leap month inserted before Adar II) is *"M05L"*. In a calendar with a leap month at the start of some years, the month code of that month would be *"M00L"*.
For example, in the Hebrew calendar, the month code of Adar (and Adar II, in leap years) is *"M06"* and the month code of Adar I (the leap month inserted before Adar II) is *"M05L"*. Theoretically, in a calendar with a leap month at the start of some years, the month code of that month would be *"M00L"*.
</emu-note>
</td>
</tr>
Expand Down Expand Up @@ -138,12 +138,6 @@ <h1>Calendar Date Records</h1>
See also ISOWeekOfYear.
</p>
<p>The Year-Week Record contains *undefined* in [[Week]] and [[Year]] field for calendars that do not have a well-defined week calendar system.</p>
<emu-note type="editor">
<p>
More details about this field will be specified in
<a href="https://tc39.es/proposal-intl-era-monthcode/">the Intl era and monthCode proposal</a>.
</p>
</emu-note>
</td>
</tr>
<tr>
Expand Down