Temporal: Copy options object in {Plain,Zoned}DateTime.{from,p.with}

This commit is contained in:
Philip Chimento 2023-03-02 17:19:24 -08:00 committed by Philip Chimento
parent 01a49502f6
commit 5ecb902a0a
14 changed files with 229 additions and 49 deletions

View File

@ -0,0 +1,34 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.from
description: overflow property is extracted with string argument.
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
];
let actual = [];
const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
const result = Temporal.PlainDateTime.from("2021-05-17T12:34:56", options);
assert.compareArray(actual, expected, "Successful call");
TemporalHelpers.assertPlainDateTime(result, 2021, 5, "M05", 17, 12, 34, 56, 0, 0, 0);
actual.splice(0); // empty it for the next check
const failureExpected = [
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
];
assert.throws(TypeError, () => Temporal.PlainDateTime.from(7, options));
assert.compareArray(actual, failureExpected, "Failing call");

View File

@ -0,0 +1,21 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.from
description: overflow property is extracted with ISO-invalid string argument.
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
];
let actual = [];
const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
assert.throws(RangeError, () => Temporal.PlainDateTime.from("2020-13-34T25:60:60", options));
assert.compareArray(actual, expected);

View File

@ -9,6 +9,12 @@ features: [Temporal]
---*/ ---*/
const expected = [ const expected = [
// CopyDataProperties
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"getOwnPropertyDescriptor options.extra",
"get options.extra",
// GetTemporalCalendarSlotValueWithISODefault // GetTemporalCalendarSlotValueWithISODefault
"get fields.calendar", "get fields.calendar",
"has fields.calendar.dateAdd", "has fields.calendar.dateAdd",
@ -67,15 +73,10 @@ const expected = [
"get fields.year.valueOf", "get fields.year.valueOf",
"call fields.year.valueOf", "call fields.year.valueOf",
// InterpretTemporalDateTimeFields // InterpretTemporalDateTimeFields
"get options.overflow",
"get options.overflow.toString", "get options.overflow.toString",
"call options.overflow.toString", "call options.overflow.toString",
"get fields.calendar.dateFromFields", "get fields.calendar.dateFromFields",
"call fields.calendar.dateFromFields", "call fields.calendar.dateFromFields",
// inside Calendar.p.dateFromFields
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
]; ];
const actual = []; const actual = [];
@ -93,7 +94,10 @@ const fields = TemporalHelpers.propertyBagObserver(actual, {
calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"), calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
}, "fields"); }, "fields");
const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); const options = TemporalHelpers.propertyBagObserver(actual, {
overflow: "constrain",
extra: "property",
}, "options");
Temporal.PlainDateTime.from(fields, options); Temporal.PlainDateTime.from(fields, options);
assert.compareArray(actual, expected, "order of operations"); assert.compareArray(actual, expected, "order of operations");

View File

@ -48,14 +48,12 @@ assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overf
assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint");
assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object"); assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object");
// toString property is read once by Calendar.dateFromFields() in the builtin // toString property should only be read and converted to a string once, because
// calendars, to get the option value for the date part, and then once again // a copied object with the resulting string on it is passed to
// internally to get the option value for the time part. // Calendar.dateFromFields().
const expected = [ const expected = [
"get overflow.toString", "get overflow.toString",
"call overflow.toString", "call overflow.toString",
"get overflow.toString",
"call overflow.toString",
]; ];
const actual = []; const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");

View File

@ -3,12 +3,16 @@
/*--- /*---
esid: sec-temporal.plaindatetime.prototype.with esid: sec-temporal.plaindatetime.prototype.with
description: The options argument is passed through to Calendar#dateFromFields as-is. description: >
The options argument is copied and the copy is passed to
Calendar#dateFromFields.
includes: [temporalHelpers.js] includes: [temporalHelpers.js]
features: [Temporal] features: [Temporal]
---*/ ---*/
const options = {}; const options = {
extra: "property",
};
let calledDateFromFields = 0; let calledDateFromFields = 0;
class Calendar extends Temporal.Calendar { class Calendar extends Temporal.Calendar {
constructor() { constructor() {
@ -16,7 +20,9 @@ class Calendar extends Temporal.Calendar {
} }
dateFromFields(fields, optionsArg) { dateFromFields(fields, optionsArg) {
++calledDateFromFields; ++calledDateFromFields;
assert.sameValue(optionsArg, options, "should pass options object through"); assert.notSameValue(optionsArg, options, "should pass copied options object");
assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object");
assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype");
return super.dateFromFields(fields, optionsArg); return super.dateFromFields(fields, optionsArg);
} }
}; };

View File

@ -12,6 +12,12 @@ const expected = [
// RejectObjectWithCalendarOrTimeZone // RejectObjectWithCalendarOrTimeZone
"get fields.calendar", "get fields.calendar",
"get fields.timeZone", "get fields.timeZone",
// CopyDataProperties
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"getOwnPropertyDescriptor options.extra",
"get options.extra",
// CalendarFields // CalendarFields
"get this.calendar.fields", "get this.calendar.fields",
"call this.calendar.fields", "call this.calendar.fields",
@ -59,15 +65,10 @@ const expected = [
"get this.calendar.mergeFields", "get this.calendar.mergeFields",
"call this.calendar.mergeFields", "call this.calendar.mergeFields",
// InterpretTemporalDateTimeFields // InterpretTemporalDateTimeFields
"get options.overflow",
"get options.overflow.toString", "get options.overflow.toString",
"call options.overflow.toString", "call options.overflow.toString",
"get this.calendar.dateFromFields", "get this.calendar.dateFromFields",
"call this.calendar.dateFromFields", "call this.calendar.dateFromFields",
// inside Calendar.p.dateFromFields
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
]; ];
const actual = []; const actual = [];
@ -96,7 +97,10 @@ const fields = TemporalHelpers.propertyBagObserver(actual, {
nanosecond: 1.7, nanosecond: 1.7,
}, "fields"); }, "fields");
const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); const options = TemporalHelpers.propertyBagObserver(actual, {
overflow: "constrain",
extra: "property",
}, "options");
instance.with(fields, options); instance.with(fields, options);
assert.compareArray(actual, expected, "order of operations"); assert.compareArray(actual, expected, "order of operations");

View File

@ -31,14 +31,12 @@ assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2 }),
assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint");
assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object"); assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object");
// toString property is read once by Calendar.dateFromFields() in the builtin // toString property should only be read and converted to a string once, because
// calendars, to get the option value for the date part, and then once again // a copied object with the resulting string on it is passed to
// internally to get the option value for the time part. // Calendar.dateFromFields().
const expected = [ const expected = [
"get overflow.toString", "get overflow.toString",
"call overflow.toString", "call overflow.toString",
"get overflow.toString",
"call overflow.toString",
]; ];
const actual = []; const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");

View File

@ -0,0 +1,49 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: options properties are extracted with string argument.
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"ownKeys options",
"getOwnPropertyDescriptor options.disambiguation",
"get options.disambiguation",
"getOwnPropertyDescriptor options.offset",
"get options.offset",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"get options.disambiguation.toString",
"call options.disambiguation.toString",
"get options.offset.toString",
"call options.offset.toString",
"get options.overflow.toString",
"call options.overflow.toString",
];
let actual = [];
const options = TemporalHelpers.propertyBagObserver(actual, {
disambiguation: "compatible",
offset: "ignore",
overflow: "reject",
}, "options");
const result = Temporal.ZonedDateTime.from("2001-09-09T01:46:40+00:00[UTC]", options);
assert.compareArray(actual, expected, "Successful call");
assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n);
actual.splice(0); // empty it for the next check
const failureExpected = [
"ownKeys options",
"getOwnPropertyDescriptor options.disambiguation",
"get options.disambiguation",
"getOwnPropertyDescriptor options.offset",
"get options.offset",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
];
assert.throws(TypeError, () => Temporal.ZonedDateTime.from(7, options));
assert.compareArray(actual, failureExpected, "Failing call");

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: options properties are extracted with ISO-invalid string argument.
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"ownKeys options",
"getOwnPropertyDescriptor options.disambiguation",
"get options.disambiguation",
"getOwnPropertyDescriptor options.offset",
"get options.offset",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
];
let actual = [];
const options = TemporalHelpers.propertyBagObserver(actual, {
disambiguation: "compatible",
offset: "ignore",
overflow: "reject",
}, "options");
assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T25:60:60+99:99[UTC]", options));
assert.compareArray(actual, expected);

View File

@ -9,6 +9,17 @@ features: [Temporal]
---*/ ---*/
const expected = [ const expected = [
// CopyDataProperties
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"getOwnPropertyDescriptor options.disambiguation",
"get options.disambiguation",
"getOwnPropertyDescriptor options.offset",
"get options.offset",
"getOwnPropertyDescriptor options.extra",
"get options.extra",
// ToTemporalCalendar
"get item.calendar", "get item.calendar",
"has item.calendar.dateAdd", "has item.calendar.dateAdd",
"has item.calendar.dateFromFields", "has item.calendar.dateFromFields",
@ -72,21 +83,14 @@ const expected = [
"has item.timeZone.getPossibleInstantsFor", "has item.timeZone.getPossibleInstantsFor",
"has item.timeZone.id", "has item.timeZone.id",
// InterpretTemporalDateTimeFields // InterpretTemporalDateTimeFields
"get options.disambiguation",
"get options.disambiguation.toString", "get options.disambiguation.toString",
"call options.disambiguation.toString", "call options.disambiguation.toString",
"get options.offset",
"get options.offset.toString", "get options.offset.toString",
"call options.offset.toString", "call options.offset.toString",
"get options.overflow",
"get options.overflow.toString", "get options.overflow.toString",
"call options.overflow.toString", "call options.overflow.toString",
"get item.calendar.dateFromFields", "get item.calendar.dateFromFields",
"call item.calendar.dateFromFields", "call item.calendar.dateFromFields",
// inside calendar.dateFromFields
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
// InterpretISODateTimeOffset // InterpretISODateTimeOffset
"get item.timeZone.getPossibleInstantsFor", "get item.timeZone.getPossibleInstantsFor",
"call item.timeZone.getPossibleInstantsFor", "call item.timeZone.getPossibleInstantsFor",
@ -116,6 +120,7 @@ function createOptionsObserver({ overflow = "constrain", disambiguation = "compa
overflow, overflow,
disambiguation, disambiguation,
offset, offset,
extra: "property",
}, "options"); }, "options");
} }

View File

@ -49,14 +49,12 @@ assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overf
assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2 }), "number"); assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2 }), "number");
assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object"); assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object");
// toString property is read once by Calendar.dateFromFields() in the builtin // toString property should only be read and converted to a string once, because
// calendars, to get the option value for the date part, and then once again // a copied object with the resulting string on it is passed to
// internally to get the option value for the time part. // Calendar.dateFromFields().
const expected = [ const expected = [
"get overflow.toString", "get overflow.toString",
"call overflow.toString", "call overflow.toString",
"get overflow.toString",
"call overflow.toString",
]; ];
const actual = []; const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");

View File

@ -0,0 +1,32 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.with
description: >
The options argument is copied and the copy is passed to
Calendar#dateFromFields.
features: [Temporal]
---*/
const options = {
extra: "property",
};
let calledDateFromFields = 0;
class Calendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
dateFromFields(fields, optionsArg) {
++calledDateFromFields;
assert.notSameValue(optionsArg, options, "should pass copied options object");
assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object");
assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype");
return super.dateFromFields(fields, optionsArg);
}
};
const calendar = new Calendar();
const datetime = new Temporal.ZonedDateTime(0n, "UTC", calendar);
const result = datetime.with({ year: 1971 }, options);
assert.sameValue(result.epochNanoseconds, 365n * 86400_000_000_000n, "year changed from 1970 to 1971")
assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once");

View File

@ -12,6 +12,16 @@ const expected = [
// RejectObjectWithCalendarOrTimeZone // RejectObjectWithCalendarOrTimeZone
"get fields.calendar", "get fields.calendar",
"get fields.timeZone", "get fields.timeZone",
// CopyDataProperties
"ownKeys options",
"getOwnPropertyDescriptor options.overflow",
"get options.overflow",
"getOwnPropertyDescriptor options.disambiguation",
"get options.disambiguation",
"getOwnPropertyDescriptor options.offset",
"get options.offset",
"getOwnPropertyDescriptor options.extra",
"get options.extra",
// GetOffsetNanosecondsFor on receiver // GetOffsetNanosecondsFor on receiver
"get this.timeZone.getOffsetNanosecondsFor", "get this.timeZone.getOffsetNanosecondsFor",
"call this.timeZone.getOffsetNanosecondsFor", "call this.timeZone.getOffsetNanosecondsFor",
@ -65,21 +75,14 @@ const expected = [
"get this.calendar.mergeFields", "get this.calendar.mergeFields",
"call this.calendar.mergeFields", "call this.calendar.mergeFields",
// InterpretTemporalDateTimeFields // InterpretTemporalDateTimeFields
"get options.disambiguation",
"get options.disambiguation.toString", "get options.disambiguation.toString",
"call options.disambiguation.toString", "call options.disambiguation.toString",
"get options.offset",
"get options.offset.toString", "get options.offset.toString",
"call options.offset.toString", "call options.offset.toString",
"get options.overflow",
"get options.overflow.toString", "get options.overflow.toString",
"call options.overflow.toString", "call options.overflow.toString",
"get this.calendar.dateFromFields", "get this.calendar.dateFromFields",
"call this.calendar.dateFromFields", "call this.calendar.dateFromFields",
// Calendar.p.dateFromFields
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
// InterpretISODateTimeOffset // InterpretISODateTimeOffset
"get this.timeZone.getPossibleInstantsFor", "get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor", "call this.timeZone.getPossibleInstantsFor",
@ -112,6 +115,7 @@ const options = TemporalHelpers.propertyBagObserver(actual, {
overflow: "constrain", overflow: "constrain",
disambiguation: "compatible", disambiguation: "compatible",
offset: "prefer", offset: "prefer",
extra: "property",
}, "options"); }, "options");
instance.with(fields, options); instance.with(fields, options);

View File

@ -31,14 +31,12 @@ assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2 }),
assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint");
assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object"); assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object");
// toString property is read once by Calendar.dateFromFields() in the builtin // toString property should only be read and converted to a string once, because
// calendars, to get the option value for the date part, and then once again // a copied object with the resulting string on it is passed to
// internally to get the option value for the time part. // Calendar.dateFromFields().
const expected = [ const expected = [
"get overflow.toString", "get overflow.toString",
"call overflow.toString", "call overflow.toString",
"get overflow.toString",
"call overflow.toString",
]; ];
const actual = []; const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");