Skip to content

Commit

Permalink
Always pass options bags through to Calendar methods.
Browse files Browse the repository at this point in the history
Instead of creating new objects and copying the options onto them, always
pass the options bags directly through to the Calendar method.

In the case of dateUntil(), when it is necessary to overwrite the
largestUnit option, copy over all own properties of the original options
object instead of setting only the largestUnit property.

Adds a few validate-and-discard steps where options values would otherwise
not be validated.

Closes: #1253
  • Loading branch information
ptomato committed Jan 26, 2021
1 parent ddda7e7 commit 757b64e
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 271 deletions.
14 changes: 13 additions & 1 deletion docs/calendar.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ Your object must not have a `calendar` property, so that it can be distinguished

The identifier of a custom calendar must consist of one or more components of between 3 and 8 ASCII alphanumeric characters each, separated by dashes, as described in [Unicode Technical Standard 35](https://unicode.org/reports/tr35/tr35.html#Unicode_locale_identifier).

Custom calendars are responsible for interpreting and validating all inputs, including options.
Calendars should (and built-in calendars will) throw a TypeError if a required option is missing or has the wrong type, but throw a RangeError if it's present but has an invalid value.

Calendars are also responsible for assigning default values.
For example, if the `overflow` option is undefined, it will be interpreted by built-in calendars as `'constrain'`.
Custom calendars should maintain this behavior unless there's a good reason not to.
Calendars can also accept additional non-default values for existing options or can accept new options that built-in calendars don't.
When adding new options, calendar authors should use a unique prefix, e.g. the name of the calendar, to avoid potential conflicts with future options which may be used by Temporal.

## Constructor

### **new Temporal.Calendar**(_calendarIdentifier_: string) : Temporal.Calendar
Expand Down Expand Up @@ -346,12 +355,15 @@ date.toString(); // => 2020-06-28[u-ca-islamic]
If either of `one` or `two` are not `Temporal.PlainDate` objects, then they will be converted to one as if they were passed to `Temporal.PlainDate.from()`.

This method does not need to be called directly except in specialized code.
It is called indirectly when using the `until()` and `since()` methods of `Temporal.PlainDateTime`, `Temporal.PlainDate`, and `Temporal.PlainYearMonth`.
It is called indirectly when using the `until()` and `since()` methods of `Temporal.PlainDateTime`, `Temporal.PlainDate`, `Temporal.PlainYearMonth`, and `Temporal.ZonedDateTime`.

If `one` is later than `two`, then the resulting duration should be negative.

The default `largestUnit` value of `'auto'` is the same as `'days'`.

> **NOTE:** Unlike `Temporal.Calendar.dateAdd()`, the `options` object that this method receives is not always the same object passed to the respective `until()` or `since()` method.
> Depending on the type, a copy may be made of the object.
For example:

```javascript
Expand Down
130 changes: 50 additions & 80 deletions polyfill/lib/ecmascript.mjs

Large diffs are not rendered by default.

17 changes: 11 additions & 6 deletions polyfill/lib/plaindate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,9 @@ export class PlainDate {
fields = ES.CalendarMergeFields(calendar, fields, props);

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);

const Construct = ES.SpeciesConstructor(this, PlainDate);
return ES.DateFromFields(calendar, fields, Construct, overflow);
return ES.DateFromFields(calendar, fields, Construct, options);
}
withCalendar(calendar) {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
Expand All @@ -150,7 +149,10 @@ export class PlainDate {
}
add(temporalDurationLike, options = undefined) {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');

let duration = ES.ToLimitedTemporalDuration(temporalDurationLike);
options = ES.NormalizeOptionsObject(options);

let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration;
ES.RejectDurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days'));
Expand All @@ -162,7 +164,10 @@ export class PlainDate {
}
subtract(temporalDurationLike, options = undefined) {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');

let duration = ES.ToLimitedTemporalDuration(temporalDurationLike);
options = ES.NormalizeOptionsObject(options);

let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration;
ES.RejectDurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days'));
Expand Down Expand Up @@ -192,7 +197,7 @@ export class PlainDate {
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false);

const result = calendar.dateUntil(this, other, { largestUnit });
const result = calendar.dateUntil(this, other, options);
if (smallestUnit === 'days' && roundingIncrement === 1) return result;

let { years, months, weeks, days } = result;
Expand Down Expand Up @@ -249,7 +254,7 @@ export class PlainDate {
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false);

let { years, months, weeks, days } = calendar.dateUntil(this, other, { largestUnit });
let { years, months, weeks, days } = calendar.dateUntil(this, other, options);
const Duration = GetIntrinsic('%Temporal.Duration%');
if (smallestUnit === 'days' && roundingIncrement === 1) {
return new Duration(-years, -months, -weeks, -days, 0, 0, 0, 0, 0, 0);
Expand Down Expand Up @@ -414,8 +419,8 @@ export class PlainDate {
}
static from(item, options = undefined) {
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
if (ES.IsTemporalDate(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
const year = GetSlot(item, ISO_YEAR);
const month = GetSlot(item, ISO_MONTH);
const day = GetSlot(item, ISO_DAY);
Expand All @@ -424,7 +429,7 @@ export class PlainDate {
if (!ES.IsTemporalDate(result)) throw new TypeError('invalid result');
return result;
}
return ES.ToTemporalDate(item, this, overflow);
return ES.ToTemporalDate(item, this, options);
}
static compare(one, two) {
one = ES.ToTemporalDate(one, PlainDate);
Expand Down
19 changes: 9 additions & 10 deletions polyfill/lib/plaindatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export class PlainDateTime {
}

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, [
'day',
Expand Down Expand Up @@ -234,7 +233,7 @@ export class PlainDateTime {
millisecond,
microsecond,
nanosecond
} = ES.InterpretTemporalDateTimeFields(calendar, fields, overflow);
} = ES.InterpretTemporalDateTimeFields(calendar, fields, options);

const Construct = ES.SpeciesConstructor(this, PlainDateTime);
const result = new Construct(
Expand Down Expand Up @@ -317,7 +316,6 @@ export class PlainDateTime {
let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration;
ES.RejectDurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
const calendar = GetSlot(this, CALENDAR);
const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.AddDateTime(
GetSlot(this, ISO_YEAR),
Expand All @@ -340,7 +338,7 @@ export class PlainDateTime {
milliseconds,
microseconds,
nanoseconds,
overflow
options
);
const Construct = ES.SpeciesConstructor(this, PlainDateTime);
const result = new Construct(
Expand All @@ -364,7 +362,6 @@ export class PlainDateTime {
let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration;
ES.RejectDurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
const calendar = GetSlot(this, CALENDAR);
const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.AddDateTime(
GetSlot(this, ISO_YEAR),
Expand All @@ -387,7 +384,7 @@ export class PlainDateTime {
-milliseconds,
-microseconds,
-nanoseconds,
overflow
options
);
const Construct = ES.SpeciesConstructor(this, PlainDateTime);
const result = new Construct(
Expand Down Expand Up @@ -454,7 +451,8 @@ export class PlainDateTime {
GetSlot(other, ISO_MICROSECOND),
GetSlot(other, ISO_NANOSECOND),
calendar,
largestUnit
largestUnit,
options
);

({
Expand Down Expand Up @@ -547,7 +545,8 @@ export class PlainDateTime {
GetSlot(other, ISO_MICROSECOND),
GetSlot(other, ISO_NANOSECOND),
calendar,
largestUnit
largestUnit,
options
);

({
Expand Down Expand Up @@ -750,8 +749,8 @@ export class PlainDateTime {

static from(item, options = undefined) {
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
if (ES.IsTemporalDateTime(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
const year = GetSlot(item, ISO_YEAR);
const month = GetSlot(item, ISO_MONTH);
const day = GetSlot(item, ISO_DAY);
Expand All @@ -766,7 +765,7 @@ export class PlainDateTime {
if (!ES.IsTemporalDateTime(result)) throw new TypeError('invalid result');
return result;
}
return ES.ToTemporalDateTime(item, this, overflow);
return ES.ToTemporalDateTime(item, this, options);
}
static compare(one, two) {
one = ES.ToTemporalDateTime(one, PlainDateTime);
Expand Down
7 changes: 3 additions & 4 deletions polyfill/lib/plainmonthday.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@ export class PlainMonthDay {
fields = ES.CalendarMergeFields(calendar, fields, props);

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);

const Construct = ES.SpeciesConstructor(this, PlainMonthDay);
const result = ES.MonthDayFromFields(calendar, fields, Construct, overflow);
const result = ES.MonthDayFromFields(calendar, fields, Construct, options);
if (!ES.IsTemporalMonthDay(result)) throw new TypeError('invalid result');
return result;
}
Expand Down Expand Up @@ -155,8 +154,8 @@ export class PlainMonthDay {
}
static from(item, options = undefined) {
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
if (ES.IsTemporalMonthDay(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
const month = GetSlot(item, ISO_MONTH);
const day = GetSlot(item, ISO_DAY);
const calendar = GetSlot(item, CALENDAR);
Expand All @@ -165,7 +164,7 @@ export class PlainMonthDay {
if (!ES.IsTemporalMonthDay(result)) throw new TypeError('invalid result');
return result;
}
return ES.ToTemporalMonthDay(item, this, overflow);
return ES.ToTemporalMonthDay(item, this, options);
}
}

Expand Down
21 changes: 10 additions & 11 deletions polyfill/lib/plainyearmonth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,9 @@ export class PlainYearMonth {
fields = ES.CalendarMergeFields(calendar, fields, props);

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);

const Construct = ES.SpeciesConstructor(this, PlainYearMonth);
return ES.YearMonthFromFields(calendar, fields, Construct, overflow);
return ES.YearMonthFromFields(calendar, fields, Construct, options);
}
add(temporalDurationLike, options = undefined) {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
Expand All @@ -120,7 +119,6 @@ export class PlainYearMonth {
({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days'));

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);

const TemporalDate = GetIntrinsic('%Temporal.PlainDate%');
const calendar = GetSlot(this, CALENDAR);
Expand All @@ -133,7 +131,7 @@ export class PlainYearMonth {
const addedDateFields = ES.ToTemporalYearMonthFields(addedDate, fieldNames);

const Construct = ES.SpeciesConstructor(this, PlainYearMonth);
return ES.YearMonthFromFields(calendar, addedDateFields, Construct, overflow);
return ES.YearMonthFromFields(calendar, addedDateFields, Construct, options);
}
subtract(temporalDurationLike, options = undefined) {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
Expand All @@ -155,7 +153,6 @@ export class PlainYearMonth {
({ days } = ES.BalanceDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'days'));

options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);

const TemporalDate = GetIntrinsic('%Temporal.PlainDate%');
const calendar = GetSlot(this, CALENDAR);
Expand All @@ -168,7 +165,7 @@ export class PlainYearMonth {
const addedDateFields = ES.ToTemporalYearMonthFields(addedDate, fieldNames);

const Construct = ES.SpeciesConstructor(this, PlainYearMonth);
return ES.YearMonthFromFields(calendar, addedDateFields, Construct, overflow);
return ES.YearMonthFromFields(calendar, addedDateFields, Construct, options);
}
until(other, options = undefined) {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
Expand Down Expand Up @@ -206,7 +203,8 @@ export class PlainYearMonth {
const otherDate = ES.DateFromFields(calendar, { ...otherFields, day: 1 }, TemporalDate);
const thisDate = ES.DateFromFields(calendar, { ...thisFields, day: 1 }, TemporalDate);

const result = calendar.dateUntil(thisDate, otherDate, { largestUnit });
const untilOptions = { ...options, largestUnit };
const result = calendar.dateUntil(thisDate, otherDate, untilOptions);
if (smallestUnit === 'months' && roundingIncrement === 1) return result;

let { years, months } = result;
Expand Down Expand Up @@ -279,7 +277,8 @@ export class PlainYearMonth {
const otherDate = ES.DateFromFields(calendar, { ...otherFields, day: 1 }, TemporalDate);
const thisDate = ES.DateFromFields(calendar, { ...thisFields, day: 1 }, TemporalDate);

let { years, months } = calendar.dateUntil(thisDate, otherDate, { largestUnit });
const untilOptions = { ...options, largestUnit };
let { years, months } = calendar.dateUntil(thisDate, otherDate, untilOptions);
const Duration = GetIntrinsic('%Temporal.Duration%');
if (smallestUnit === 'months' && roundingIncrement === 1) {
return new Duration(-years, -months, 0, 0, 0, 0, 0, 0, 0, 0);
Expand Down Expand Up @@ -361,7 +360,7 @@ export class PlainYearMonth {
ObjectAssign(fields, ES.ToRecord(item, entries));

const Date = GetIntrinsic('%Temporal.PlainDate%');
return ES.DateFromFields(calendar, fields, Date, 'reject');
return ES.DateFromFields(calendar, fields, Date, { overflow: 'reject' });
}
getISOFields() {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
Expand All @@ -374,8 +373,8 @@ export class PlainYearMonth {
}
static from(item, options = undefined) {
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
if (ES.IsTemporalYearMonth(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
const year = GetSlot(item, ISO_YEAR);
const month = GetSlot(item, ISO_MONTH);
const calendar = GetSlot(item, CALENDAR);
Expand All @@ -384,7 +383,7 @@ export class PlainYearMonth {
if (!ES.IsTemporalYearMonth(result)) throw new TypeError('invalid result');
return result;
}
return ES.ToTemporalYearMonth(item, this, overflow);
return ES.ToTemporalYearMonth(item, this, options);
}
static compare(one, two) {
one = ES.ToTemporalYearMonth(one, PlainYearMonth);
Expand Down
Loading

0 comments on commit 757b64e

Please sign in to comment.