Temporal: Limit allowed values for Calendar.fields to date units

This commit is contained in:
Philip Chimento 2023-03-02 12:55:38 -08:00 committed by Philip Chimento
parent c372de0bbe
commit 4b4de778d4
25 changed files with 29 additions and 146 deletions

View File

@ -14,7 +14,7 @@ features: [Temporal]
const calendar = new Temporal.Calendar("iso8601"); const calendar = new Temporal.Calendar("iso8601");
assert.throws(RangeError, () => calendar.fields(["day", "month", "day"])); assert.throws(RangeError, () => calendar.fields(["day", "month", "day"]));
assert.throws(RangeError, () => calendar.fields(["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "year"])); assert.throws(RangeError, () => calendar.fields(["year", "month", "monthCode", "day", "year"]));
const manyFields = { const manyFields = {
count: 0 count: 0

View File

@ -3,7 +3,9 @@
/*--- /*---
esid: sec-temporal.calendar.prototype.fields esid: sec-temporal.calendar.prototype.fields
description: Calendar.prototype.fields rejects input field names that are not singular names of temporal units description: >
Calendar.prototype.fields rejects input field names that are not singular
names of Temporal date units
info: | info: |
sec-temporal.calendar.prototype.fields step 7.b.ii: sec-temporal.calendar.prototype.fields step 7.b.ii:
7. Repeat, while next is not false, 7. Repeat, while next is not false,
@ -14,7 +16,7 @@ info: |
1. Let completion be ThrowCompletion(a newly created TypeError object). 1. Let completion be ThrowCompletion(a newly created TypeError object).
2. Return ? IteratorClose(iteratorRecord, completion). 2. Return ? IteratorClose(iteratorRecord, completion).
sec-temporal.calendar.prototype.fields step 7.b.iv: sec-temporal.calendar.prototype.fields step 7.b.iv:
iv. If _nextValue_ is not one of *"year"*, *"month"*, *"monthCode"*, *"day"*, *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"*, then iv. If _nextValue_ is not one of *"year"*, *"month"*, *"monthCode"*, or *"day"*, then
1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object). 1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object).
2. Return ? IteratorClose(_iteratorRecord_, _completion_). 2. Return ? IteratorClose(_iteratorRecord_, _completion_).
@ -30,6 +32,12 @@ assert.throws(TypeError, () => calendar.fields([null]));
assert.throws(TypeError, () => calendar.fields([{}])); assert.throws(TypeError, () => calendar.fields([{}]));
assert.throws(TypeError, () => calendar.fields([undefined])); assert.throws(TypeError, () => calendar.fields([undefined]));
assert.throws(TypeError, () => calendar.fields(["day", 1])); assert.throws(TypeError, () => calendar.fields(["day", 1]));
assert.throws(RangeError, () => calendar.fields(["hour"]));
assert.throws(RangeError, () => calendar.fields(["minute"]));
assert.throws(RangeError, () => calendar.fields(["second"]));
assert.throws(RangeError, () => calendar.fields(["millisecond"]));
assert.throws(RangeError, () => calendar.fields(["microsecond"]));
assert.throws(RangeError, () => calendar.fields(["nanosecond"]));
assert.throws(RangeError, () => calendar.fields(["month", "days"])); assert.throws(RangeError, () => calendar.fields(["month", "days"]));
assert.throws(RangeError, () => calendar.fields(["days"])); assert.throws(RangeError, () => calendar.fields(["days"]));
assert.throws(RangeError, () => calendar.fields(["people"])); assert.throws(RangeError, () => calendar.fields(["people"]));

View File

@ -17,7 +17,7 @@ info: |
a. Set next to ? IteratorStep(iteratorRecord). a. Set next to ? IteratorStep(iteratorRecord).
b. If next is not false, then b. If next is not false, then
i. Let nextValue be ? IteratorValue(next). i. Let nextValue be ? IteratorValue(next).
iv. If nextValue is not one of "year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", then iv. If nextValue is not one of "year", "month", "monthCode", or "day", then
1. Let completion be ThrowCompletion(a newly created RangeError object). 1. Let completion be ThrowCompletion(a newly created RangeError object).
2. Return ? IteratorClose(iteratorRecord, completion). 2. Return ? IteratorClose(iteratorRecord, completion).
features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators] features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators]
@ -38,7 +38,7 @@ const fields = {
// The following three lines should not be reached if the implemention // The following three lines should not be reached if the implemention
// correctly check the previous line. // correctly check the previous line.
i++; i++;
yield "hour"; yield "day";
i++; i++;
} }
} }

View File

@ -38,9 +38,8 @@ assert.throws(
RangeError, () => cal.fields(fields), "repeated valid value should throw"); RangeError, () => cal.fields(fields), "repeated valid value should throw");
assert.sameValue(i, 2, "Should stop at 2"); assert.sameValue(i, 2, "Should stop at 2");
// Test all valid value will throw while repeate // Test all valid values will throw when repeated
[ "nanosecond", "microsecond", "millisecond", "second", [ "day", "monthCode", "month", "year" ].forEach((f) => {
"minute", "hour", "day", "monthCode", "month", "year" ].forEach((f) => {
i = 0; i = 0;
const fields2 = { const fields2 = {
*[Symbol.iterator]() { *[Symbol.iterator]() {

View File

@ -17,7 +17,7 @@ info: |
a. Set next to ? IteratorStep(iteratorRecord). a. Set next to ? IteratorStep(iteratorRecord).
b. If next is not false, then b. If next is not false, then
i. Let nextValue be ? IteratorValue(next). i. Let nextValue be ? IteratorValue(next).
iv. If nextValue is not one of "year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", then iv. If nextValue is not one of "year", "month", "monthCode", or "day", then
1. Let completion be ThrowCompletion(a newly created RangeError object). 1. Let completion be ThrowCompletion(a newly created RangeError object).
2. Return ? IteratorClose(iteratorRecord, completion). 2. Return ? IteratorClose(iteratorRecord, completion).
features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators] features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators]
@ -26,12 +26,6 @@ includes: [compareArray.js]
let cal = new Temporal.Calendar("iso8601") let cal = new Temporal.Calendar("iso8601")
const fields = { const fields = {
*[Symbol.iterator]() { *[Symbol.iterator]() {
yield "nanosecond";
yield "microsecond";
yield "millisecond";
yield "second";
yield "minute";
yield "hour";
yield "day"; yield "day";
yield "monthCode"; yield "monthCode";
yield "month"; yield "month";

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -18,14 +18,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -6,7 +6,7 @@ esid: sec-temporal.plaindatetime.prototype.with
description: Verify the result of calendar.fields() is treated correctly. description: Verify the result of calendar.fields() is treated correctly.
info: | info: |
sec-temporal.plaindatetime.prototype.with step 9: sec-temporal.plaindatetime.prototype.with step 9:
9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
sec-temporal-calendarfields step 4: sec-temporal-calendarfields step 4:
4. Let _result_ be ? IterableToList(_fieldsArray_). 4. Let _result_ be ? IterableToList(_fieldsArray_).
includes: [compareArray.js, temporalHelpers.js] includes: [compareArray.js, temporalHelpers.js]
@ -15,14 +15,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -18,14 +18,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -17,14 +17,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -6,7 +6,7 @@ esid: sec-temporal.zoneddatetime.prototype.with
description: Verify the result of calendar.fields() is treated correctly. description: Verify the result of calendar.fields() is treated correctly.
info: | info: |
sec-temporal.zoneddatetime.prototype.with step 9: sec-temporal.zoneddatetime.prototype.with step 9:
9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
sec-temporal-calendarfields step 4: sec-temporal-calendarfields step 4:
4. Let _result_ be ? IterableToList(_fieldsArray_). 4. Let _result_ be ? IterableToList(_fieldsArray_).
includes: [compareArray.js, temporalHelpers.js] includes: [compareArray.js, temporalHelpers.js]
@ -15,14 +15,8 @@ features: [Temporal]
const expected = [ const expected = [
"day", "day",
"hour",
"microsecond",
"millisecond",
"minute",
"month", "month",
"monthCode", "monthCode",
"nanosecond",
"second",
"year", "year",
]; ];

View File

@ -5,16 +5,12 @@
esid: sec-temporal.zoneddatetime.protoype.with esid: sec-temporal.zoneddatetime.protoype.with
description: TypeError thrown when the offset field of the receiver is broken description: TypeError thrown when the offset field of the receiver is broken
info: | info: |
10. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
11. Append *"offset"* to _fieldNames_. 8. Append *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"nanosecond"*, *"offset"*, and *"second"* to _fieldNames_.
12. Let _partialZonedDateTime_ be ? PreparePartialTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_). 9. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"offset"* »).
... 10. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~).
17. Append *"timeZone"* to _fieldNames_. 11. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_).
18. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"timeZone"*, *"offset"* »). 12. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"offset"* »).
19. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_).
20. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"timeZone"*, *"offset"* »).
21. Let _offsetString_ be ! Get(_fields_, *"offset"*).
22. Assert: Type(_offsetString_) is String.
features: [Temporal] features: [Temporal]
---*/ ---*/
@ -43,21 +39,21 @@ class ObservedCalendar extends Temporal.Calendar {
const calendar = new ObservedCalendar(); const calendar = new ObservedCalendar();
const dateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); const dateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
// Test throw in step 12 // Test throw in step 10
assert.throws(TypeError, () => dateTime.with({ offset: Symbol("can't convert to string") }), "conversion failure on ZonedDateTime-like"); assert.throws(TypeError, () => dateTime.with({ offset: Symbol("can't convert to string") }), "conversion failure on ZonedDateTime-like");
assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not be called"); assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not be called");
calendar.resetCalls(); calendar.resetCalls();
// Test throw in step 20 (before sabotaging the ZonedDateTime instance) // Test throw in step 12 (before sabotaging the ZonedDateTime instance)
assert.throws(TypeError, () => dateTime.with({ year: 2002 }), "conversion failure on sabotaged return value from mergeFields"); assert.throws(TypeError, () => dateTime.with({ year: 2002 }), "conversion failure on sabotaged return value from mergeFields");
assert.sameValue(calendar.mergeFieldsCalled, 1, "calendar.mergeFields was called once"); assert.sameValue(calendar.mergeFieldsCalled, 1, "calendar.mergeFields was called once");
calendar.resetCalls(); calendar.resetCalls();
// Test throw in step 18 // Test throw in step 9
Object.defineProperty(dateTime, "offset", { value: Symbol("can't convert to string"), configurable: true }); Object.defineProperty(dateTime, "offset", { value: Symbol("can't convert to string"), configurable: true });
@ -66,7 +62,7 @@ assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not
calendar.resetCalls(); calendar.resetCalls();
// Test offset being required in step 18 // Test offset being required in step 9
Object.defineProperty(dateTime, "offset", { value: undefined }); Object.defineProperty(dateTime, "offset", { value: undefined });