From 292b17fc084c0082cf082ec7bbc111039d80f038 Mon Sep 17 00:00:00 2001
From: Philip Chimento
Date: Sat, 17 Oct 2020 12:14:02 -0700
Subject: [PATCH] Add Calendar.fields method
The Calendar.fields method is called whenever Temporal needs to determine
if a calendar object requires extra fields to uniquely identify its date.
Closes: #666
---
docs/calendar-draft.md | 7 ++
docs/calendar.md | 24 ++++++
polyfill/index.d.ts | 2 +
polyfill/lib/calendar.mjs | 11 ++-
polyfill/lib/date.mjs | 19 +++--
polyfill/lib/datetime.mjs | 19 +++--
polyfill/lib/ecmascript.mjs | 105 +++++++++++++++++++----
polyfill/lib/monthday.mjs | 19 +++--
polyfill/lib/yearmonth.mjs | 30 ++++---
polyfill/test/usercalendar.mjs | 105 +++++++++++++++++++++++
spec/abstractops.html | 3 +-
spec/calendar.html | 32 +++++++
spec/date.html | 95 ++++++++-------------
spec/datetime.html | 148 +++++++++------------------------
spec/monthday.html | 95 +++++++++------------
spec/yearmonth.html | 99 +++++++++-------------
16 files changed, 475 insertions(+), 338 deletions(-)
diff --git a/docs/calendar-draft.md b/docs/calendar-draft.md
index 977a1a42c9..e1b48871a0 100644
--- a/docs/calendar-draft.md
+++ b/docs/calendar-draft.md
@@ -74,6 +74,10 @@ class Temporal.Calendar {
/** A string identifier for this calendar */
id : string;
+ fields(
+ fields: array
+ ) : array;
+
//////////////////
// Arithmetic //
//////////////////
@@ -151,6 +155,9 @@ get foo(...args) {
Calendars can add additional *calendar-specific accessors*, such as the year type ("kesidran", "chaser", "maleh") in the Hebrew calendar, and may add conforming accessor methods to Temporal.Date.prototype.
+If any of these accessors are needed for constructing a Temporal.Date from fields, then the calendar should implement `fields()` which, given an array of field names in the ISO calendar, returns an array of equivalent field names in the calendar.
+We are not aware of this being necessary for any built-in calendars.
+
An instance of `MyCalendar` is *expected* to have stateless behavior; i.e., calling a method with the same arguments should return the same result each time. There would be no mechanism for enforcing that user-land calendars are stateless; the calendar author should test this expectation on their own in order to prevent unexpected behavior such as the lack of round-tripping.
### Enumerable Properties
diff --git a/docs/calendar.md b/docs/calendar.md
index 80766240c9..d40f90fac8 100644
--- a/docs/calendar.md
+++ b/docs/calendar.md
@@ -293,6 +293,30 @@ Temporal.Calendar.from('chinese').dateDifference(
) // => P1M2D
```
+### calendar.**fields**(fields: array) : array
+
+**Parameters:**
+
+- `fields` (array of strings): A list of field names.
+
+**Returns:** a new list of field names.
+
+This method does not need to be called directly except in specialized code.
+It is called indirectly when using the `from()` static methods and `with()` methods of `Temporal.DateTime`, `Temporal.Date`, and `Temporal.YearMonth`.
+
+Custom calendars should override this method if they require more fields with which to denote the date than the standard `era`, `year`, `month`, and `day`.
+The input array contains the field names that are necessary for a particular operation (for example, `'month'` and `'day'` for `Temporal.MonthDay.prototype.with()`), and the method should make a copy of the array and add whichever extra fields are necessary.
+
+The default implementation of this method returns a copy of `fields`.
+
+Usage example:
+
+```js
+// In built-in calendars, this method just makes a copy of the input array
+Temporal.Calendar.from('iso8601').fields(['month', 'day']);
+ // => ['month', 'day']
+```
+
### calendar.**toString**() : string
**Returns:** The string given by `calendar.id`.
diff --git a/polyfill/index.d.ts b/polyfill/index.d.ts
index cecce6d1db..71300ddbc2 100644
--- a/polyfill/index.d.ts
+++ b/polyfill/index.d.ts
@@ -484,6 +484,7 @@ export namespace Temporal {
| /** @deprecated */ 'day'
>
): Temporal.Duration;
+ fields?(fields: Array): Array;
}
/**
@@ -550,6 +551,7 @@ export namespace Temporal {
| /** @deprecated */ 'day'
>
): Temporal.Duration;
+ fields(fields: Array): Array;
toString(): string;
}
diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs
index c70958db82..d2e13992c4 100644
--- a/polyfill/lib/calendar.mjs
+++ b/polyfill/lib/calendar.mjs
@@ -44,6 +44,10 @@ export class Calendar {
void constructor;
throw new Error('not implemented');
}
+ fields(fields) {
+ if (!ES.IsTemporalCalendar(this)) throw new TypeError('invalid receiver');
+ return ES.CreateListFromArrayLike(fields, ['String']);
+ }
dateAdd(date, duration, options, constructor) {
void date;
void duration;
@@ -137,6 +141,7 @@ export class Calendar {
MakeIntrinsicClass(Calendar, 'Temporal.Calendar');
DefineIntrinsic('Temporal.Calendar.from', Calendar.from);
+DefineIntrinsic('Temporal.Calendar.prototype.fields', Calendar.prototype.fields);
DefineIntrinsic('Temporal.Calendar.prototype.toString', Calendar.prototype.toString);
class ISO8601Calendar extends Calendar {
@@ -149,7 +154,7 @@ class ISO8601Calendar extends Calendar {
if (!ES.IsTemporalCalendar(this)) throw new TypeError('invalid receiver');
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
- let { year, month, day } = ES.ToTemporalDateRecord(fields);
+ let { year, month, day } = ES.ToRecord(fields, [['day'], ['month'], ['year']]);
({ year, month, day } = ES.RegulateDate(year, month, day, overflow));
return new constructor(year, month, day, this);
}
@@ -157,7 +162,7 @@ class ISO8601Calendar extends Calendar {
if (!ES.IsTemporalCalendar(this)) throw new TypeError('invalid receiver');
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
- let { year, month } = ES.ToTemporalYearMonthRecord(fields);
+ let { year, month } = ES.ToRecord(fields, [['month'], ['year']]);
({ year, month } = ES.RegulateYearMonth(year, month, overflow));
return new constructor(year, month, this, /* referenceISODay = */ 1);
}
@@ -165,7 +170,7 @@ class ISO8601Calendar extends Calendar {
if (!ES.IsTemporalCalendar(this)) throw new TypeError('invalid receiver');
options = ES.NormalizeOptionsObject(options);
const overflow = ES.ToTemporalOverflow(options);
- let { month, day } = ES.ToTemporalMonthDayRecord(fields);
+ let { month, day } = ES.ToRecord(fields, [['day'], ['month']]);
({ month, day } = ES.RegulateMonthDay(month, day, overflow));
return new constructor(month, day, this, /* referenceISOYear = */ 1972);
}
diff --git a/polyfill/lib/date.mjs b/polyfill/lib/date.mjs
index 0a55a3c0ea..494791f005 100644
--- a/polyfill/lib/date.mjs
+++ b/polyfill/lib/date.mjs
@@ -124,11 +124,12 @@ export class Date {
calendar = GetSlot(this, CALENDAR);
source = this;
}
- const props = ES.ToPartialRecord(temporalDateLike, ['day', 'era', 'month', 'year']);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const props = ES.ToPartialRecord(temporalDateLike, fieldNames);
if (!props) {
throw new TypeError('invalid date-like');
}
- const fields = ES.ToTemporalDateRecord(source);
+ const fields = ES.ToTemporalDateFields(source, fieldNames);
ObjectAssign(fields, props);
const Construct = ES.SpeciesConstructor(this, Date);
const result = calendar.dateFromFields(fields, options, Construct);
@@ -273,20 +274,24 @@ export class Date {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const YearMonth = GetIntrinsic('%Temporal.YearMonth%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalDateRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(this, fieldNames);
return calendar.yearMonthFromFields(fields, {}, YearMonth);
}
toMonthDay() {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const MonthDay = GetIntrinsic('%Temporal.MonthDay%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalDateRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(this, fieldNames);
return calendar.monthDayFromFields(fields, {}, MonthDay);
}
getFields() {
- const fields = ES.ToTemporalDateRecord(this);
- if (!fields) throw new TypeError('invalid receiver');
- fields.calendar = GetSlot(this, CALENDAR);
+ if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(this, fieldNames);
+ fields.calendar = calendar;
return fields;
}
getISOFields() {
diff --git a/polyfill/lib/datetime.mjs b/polyfill/lib/datetime.mjs
index ffdcf7a561..83f2254285 100644
--- a/polyfill/lib/datetime.mjs
+++ b/polyfill/lib/datetime.mjs
@@ -215,7 +215,7 @@ export class DateTime {
calendar = GetSlot(this, CALENDAR);
source = this;
}
- const props = ES.ToPartialRecord(temporalDateTimeLike, [
+ const fieldNames = ES.CalendarFields(calendar, [
'day',
'era',
'hour',
@@ -227,10 +227,11 @@ export class DateTime {
'second',
'year'
]);
+ const props = ES.ToPartialRecord(temporalDateTimeLike, fieldNames);
if (!props) {
throw new TypeError('invalid date-time-like');
}
- const fields = ES.ToTemporalDateTimeRecord(source);
+ const fields = ES.ToTemporalDateTimeFields(source, fieldNames);
ObjectAssign(fields, props);
const date = calendar.dateFromFields(fields, options, GetIntrinsic('%Temporal.Date%'));
let year = GetSlot(date, ISO_YEAR);
@@ -622,14 +623,16 @@ export class DateTime {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const YearMonth = GetIntrinsic('%Temporal.YearMonth%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalDateTimeRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(this, fieldNames);
return calendar.yearMonthFromFields(fields, {}, YearMonth);
}
toMonthDay() {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const MonthDay = GetIntrinsic('%Temporal.MonthDay%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalDateTimeRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(this, fieldNames);
return calendar.monthDayFromFields(fields, {}, MonthDay);
}
toTime() {
@@ -637,9 +640,11 @@ export class DateTime {
return ES.TemporalDateTimeToTime(this);
}
getFields() {
- const fields = ES.ToTemporalDateTimeRecord(this);
- if (!fields) throw new TypeError('invalid receiver');
- fields.calendar = GetSlot(this, CALENDAR);
+ if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateTimeFields(this, fieldNames);
+ fields.calendar = calendar;
return fields;
}
getISOFields() {
diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs
index e5aa427a11..aa33ef69f2 100644
--- a/polyfill/lib/ecmascript.mjs
+++ b/polyfill/lib/ecmascript.mjs
@@ -1,3 +1,6 @@
+const ArrayIsArray = Array.isArray;
+const ArrayPrototypeIndexOf = Array.prototype.indexOf;
+const ArrayPrototypePush = Array.prototype.push;
const IntlDateTimeFormat = globalThis.Intl.DateTimeFormat;
const MathAbs = Math.abs;
const MathCeil = Math.ceil;
@@ -12,6 +15,7 @@ import bigInt from 'big-integer';
import Call from 'es-abstract/2020/Call.js';
import SpeciesConstructor from 'es-abstract/2020/SpeciesConstructor.js';
import ToInteger from 'es-abstract/2020/ToInteger.js';
+import ToLength from 'es-abstract/2020/ToLength.js';
import ToNumber from 'es-abstract/2020/ToNumber.js';
import ToPrimitive from 'es-abstract/2020/ToPrimitive.js';
import ToString from 'es-abstract/2020/ToString.js';
@@ -63,6 +67,7 @@ const ES2020 = {
Call,
SpeciesConstructor,
ToInteger,
+ ToLength,
ToNumber,
ToPrimitive,
ToString,
@@ -638,7 +643,8 @@ export const ES = ObjectAssign({}, ES2020, {
calendar = relativeTo.calendar;
if (calendar === undefined) calendar = GetISO8601Calendar();
calendar = ES.ToTemporalCalendar(calendar);
- const fields = ES.ToTemporalDateTimeRecord(relativeTo);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateTimeFields(relativeTo, fieldNames);
const TemporalDate = GetIntrinsic('%Temporal.Date%');
const date = calendar.dateFromFields(fields, {}, TemporalDate);
year = GetSlot(date, ISO_YEAR);
@@ -761,11 +767,18 @@ export const ES = ObjectAssign({}, ES2020, {
return result;
},
// field access in the following operations is intentionally alphabetical
- ToTemporalDateRecord: (bag) => {
- return ES.ToRecord(bag, [['day'], ['era', undefined], ['month'], ['year']]);
+ ToTemporalDateFields: (bag, fieldNames) => {
+ const entries = [['day'], ['era', undefined], ['month'], ['year']];
+ // Add extra fields from the calendar at the end
+ fieldNames.forEach((fieldName) => {
+ if (!entries.some(([name]) => name === fieldName)) {
+ entries.push([fieldName, undefined]);
+ }
+ });
+ return ES.ToRecord(bag, entries);
},
- ToTemporalDateTimeRecord: (bag) => {
- return ES.ToRecord(bag, [
+ ToTemporalDateTimeFields: (bag, fieldNames) => {
+ const entries = [
['day'],
['era', undefined],
['hour', 0],
@@ -776,10 +789,24 @@ export const ES = ObjectAssign({}, ES2020, {
['nanosecond', 0],
['second', 0],
['year']
- ]);
- },
- ToTemporalMonthDayRecord: (bag) => {
- return ES.ToRecord(bag, [['day'], ['month']]);
+ ];
+ // Add extra fields from the calendar at the end
+ fieldNames.forEach((fieldName) => {
+ if (!entries.some(([name]) => name === fieldName)) {
+ entries.push([fieldName, undefined]);
+ }
+ });
+ return ES.ToRecord(bag, entries);
+ },
+ ToTemporalMonthDayFields: (bag, fieldNames) => {
+ const entries = [['day'], ['month']];
+ // Add extra fields from the calendar at the end
+ fieldNames.forEach((fieldName) => {
+ if (!entries.some(([name]) => name === fieldName)) {
+ entries.push([fieldName, undefined]);
+ }
+ });
+ return ES.ToRecord(bag, entries);
},
ToTemporalTimeRecord: (bag) => {
const props = ES.ToPartialRecord(bag, ['hour', 'microsecond', 'millisecond', 'minute', 'nanosecond', 'second']);
@@ -787,8 +814,15 @@ export const ES = ObjectAssign({}, ES2020, {
const { hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0 } = props;
return { hour, minute, second, millisecond, microsecond, nanosecond };
},
- ToTemporalYearMonthRecord: (bag) => {
- return ES.ToRecord(bag, [['era', undefined], ['month'], ['year']]);
+ ToTemporalYearMonthFields: (bag, fieldNames) => {
+ const entries = [['era', undefined], ['month'], ['year']];
+ // Add extra fields from the calendar at the end
+ fieldNames.forEach((fieldName) => {
+ if (!entries.some(([name]) => name === fieldName)) {
+ entries.push([fieldName, undefined]);
+ }
+ });
+ return ES.ToRecord(bag, entries);
},
ToTemporalDate: (item, constructor, overflow = 'constrain') => {
@@ -798,7 +832,8 @@ export const ES = ObjectAssign({}, ES2020, {
let calendar = item.calendar;
if (calendar === undefined) calendar = new (GetIntrinsic('%Temporal.ISO8601Calendar%'))();
calendar = ES.ToTemporalCalendar(calendar);
- const fields = ES.ToTemporalDateRecord(item);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateFields(item, fieldNames);
result = calendar.dateFromFields(fields, { overflow }, constructor);
} else {
let { year, month, day, calendar } = ES.ParseTemporalDateString(ES.ToString(item));
@@ -817,7 +852,8 @@ export const ES = ObjectAssign({}, ES2020, {
calendar = item.calendar;
if (calendar === undefined) calendar = new (GetIntrinsic('%Temporal.ISO8601Calendar%'))();
calendar = ES.ToTemporalCalendar(calendar);
- const fields = ES.ToTemporalDateTimeRecord(item);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'era', 'month', 'year']);
+ const fields = ES.ToTemporalDateTimeFields(item, fieldNames);
const TemporalDate = GetIntrinsic('%Temporal.Date%');
const date = calendar.dateFromFields(fields, { overflow }, TemporalDate);
year = GetSlot(date, ISO_YEAR);
@@ -924,7 +960,8 @@ export const ES = ObjectAssign({}, ES2020, {
let calendar = item.calendar;
if (calendar === undefined) calendar = new (GetIntrinsic('%Temporal.ISO8601Calendar%'))();
calendar = ES.ToTemporalCalendar(calendar);
- const fields = ES.ToTemporalMonthDayRecord(item);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'month']);
+ const fields = ES.ToTemporalMonthDayFields(item, fieldNames);
result = calendar.monthDayFromFields(fields, { overflow }, constructor);
} else {
let { month, day, referenceISOYear, calendar } = ES.ParseTemporalMonthDayString(ES.ToString(item));
@@ -965,7 +1002,8 @@ export const ES = ObjectAssign({}, ES2020, {
let calendar = item.calendar;
if (calendar === undefined) calendar = new (GetIntrinsic('%Temporal.ISO8601Calendar%'))();
calendar = ES.ToTemporalCalendar(calendar);
- const fields = ES.ToTemporalYearMonthRecord(item);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const fields = ES.ToTemporalYearMonthFields(item, fieldNames);
result = calendar.yearMonthFromFields(fields, { overflow }, constructor);
} else {
let { year, month, referenceISODay, calendar } = ES.ParseTemporalYearMonthString(ES.ToString(item));
@@ -991,7 +1029,12 @@ export const ES = ObjectAssign({}, ES2020, {
}
return calendar;
},
-
+ CalendarFields: (calendar, fieldNames) => {
+ let fields = calendar.fields;
+ if (fields === undefined) fields = GetIntrinsic('%Temporal.Calendar.prototype.fields%');
+ const array = ES.Call(fields, calendar, [fieldNames]);
+ return ES.CreateListFromArrayLike(array, ['String']);
+ },
CalendarToString: (calendar) => {
let toString = calendar.toString;
if (toString === undefined) toString = GetIntrinsic('%Temporal.Calendar.prototype.toString%');
@@ -2590,6 +2633,36 @@ export const ES = ObjectAssign({}, ES2020, {
throw new RangeError(`${property} must be between ${minimum} and ${maximum}, not ${value}`);
}
return MathFloor(value);
+ },
+ // Following two operations are overridden because the es-abstract version of
+ // ES.Get() unconditionally uses util.inspect
+ LengthOfArrayLike: (obj) => {
+ if (ES.Type(obj) !== 'Object') {
+ throw new TypeError('Assertion failed: `obj` must be an Object');
+ }
+ return ES.ToLength(obj.length);
+ },
+ CreateListFromArrayLike: (obj, elementTypes) => {
+ if (ES.Type(obj) !== 'Object') {
+ throw new TypeError('Assertion failed: `obj` must be an Object');
+ }
+ if (!ArrayIsArray(elementTypes)) {
+ throw new TypeError('Assertion failed: `elementTypes`, if provided, must be an array');
+ }
+ var len = ES.LengthOfArrayLike(obj);
+ var list = [];
+ var index = 0;
+ while (index < len) {
+ var indexName = ES.ToString(index);
+ var next = obj[indexName];
+ var nextType = ES.Type(next);
+ if (ArrayPrototypeIndexOf.call(elementTypes, nextType) < 0) {
+ throw new TypeError(`item type ${nextType} is not a valid elementType`);
+ }
+ ArrayPrototypePush.call(list, next);
+ index += 1;
+ }
+ return list;
}
});
diff --git a/polyfill/lib/monthday.mjs b/polyfill/lib/monthday.mjs
index 49e59d3e38..a40719515e 100644
--- a/polyfill/lib/monthday.mjs
+++ b/polyfill/lib/monthday.mjs
@@ -64,14 +64,16 @@ export class MonthDay {
if ('calendar' in temporalMonthDayLike) {
throw new RangeError('invalid calendar property in month-day-like');
}
- const props = ES.ToPartialRecord(temporalMonthDayLike, ['day', 'month']);
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'month']);
+ const props = ES.ToPartialRecord(temporalMonthDayLike, fieldNames);
if (!props) {
throw new TypeError('invalid month-day-like');
}
- const fields = ES.ToTemporalMonthDayRecord(this);
+ const fields = ES.ToTemporalMonthDayFields(this, fieldNames);
ObjectAssign(fields, props);
const Construct = ES.SpeciesConstructor(this, MonthDay);
- const result = GetSlot(this, CALENDAR).monthDayFromFields(fields, options, Construct);
+ const result = calendar.monthDayFromFields(fields, options, Construct);
if (!ES.IsTemporalMonthDay(result)) throw new TypeError('invalid result');
return result;
}
@@ -109,14 +111,17 @@ export class MonthDay {
year = ES.ToInteger(item);
}
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalMonthDayRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'month']);
+ const fields = ES.ToTemporalMonthDayFields(this, fieldNames);
const Date = GetIntrinsic('%Temporal.Date%');
return calendar.dateFromFields({ ...fields, era, year }, options, Date);
}
getFields() {
- const fields = ES.ToTemporalMonthDayRecord(this);
- if (!fields) throw new TypeError('invalid receiver');
- fields.calendar = GetSlot(this, CALENDAR);
+ if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['day', 'month']);
+ const fields = ES.ToTemporalMonthDayFields(this, fieldNames);
+ fields.calendar = calendar;
return fields;
}
getISOFields() {
diff --git a/polyfill/lib/yearmonth.mjs b/polyfill/lib/yearmonth.mjs
index 08da952675..f74da2774f 100644
--- a/polyfill/lib/yearmonth.mjs
+++ b/polyfill/lib/yearmonth.mjs
@@ -81,14 +81,16 @@ export class YearMonth {
if ('calendar' in temporalYearMonthLike) {
throw new RangeError('invalid calendar property in year-month-like');
}
- const props = ES.ToPartialRecord(temporalYearMonthLike, ['era', 'month', 'year']);
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const props = ES.ToPartialRecord(temporalYearMonthLike, fieldNames);
if (!props) {
throw new TypeError('invalid year-month-like');
}
- const fields = ES.ToTemporalYearMonthRecord(this);
+ const fields = ES.ToTemporalYearMonthFields(this, fieldNames);
ObjectAssign(fields, props);
const Construct = ES.SpeciesConstructor(this, YearMonth);
- const result = GetSlot(this, CALENDAR).yearMonthFromFields(fields, options, Construct);
+ const result = calendar.yearMonthFromFields(fields, options, Construct);
if (!ES.IsTemporalYearMonth(result)) throw new TypeError('invalid result');
return result;
}
@@ -101,7 +103,8 @@ export class YearMonth {
const TemporalDate = GetIntrinsic('%Temporal.Date%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalYearMonthRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const fields = ES.ToTemporalYearMonthFields(this, fieldNames);
const sign = ES.DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0);
const day = sign < 0 ? calendar.daysInMonth(this) : 1;
const startDate = calendar.dateFromFields({ ...fields, day }, {}, TemporalDate);
@@ -121,7 +124,8 @@ export class YearMonth {
const TemporalDate = GetIntrinsic('%Temporal.Date%');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalYearMonthRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const fields = ES.ToTemporalYearMonthFields(this, fieldNames);
const sign = ES.DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0);
const day = sign < 0 ? 1 : calendar.daysInMonth(this);
const startDate = calendar.dateFromFields({ ...fields, day }, {}, TemporalDate);
@@ -159,8 +163,9 @@ export class YearMonth {
const roundingMode = ES.ToTemporalRoundingMode(options, 'nearest');
const roundingIncrement = ES.ToTemporalRoundingIncrement(options, undefined, false);
- const otherFields = ES.ToTemporalYearMonthRecord(other);
- const thisFields = ES.ToTemporalYearMonthRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const otherFields = ES.ToTemporalYearMonthFields(other, fieldNames);
+ const thisFields = ES.ToTemporalYearMonthFields(this, fieldNames);
const TemporalDate = GetIntrinsic('%Temporal.Date%');
const otherDate = calendar.dateFromFields({ ...otherFields, day: 1 }, {}, TemporalDate);
const thisDate = calendar.dateFromFields({ ...thisFields, day: 1 }, {}, TemporalDate);
@@ -230,14 +235,17 @@ export class YearMonth {
toDateOnDay(day) {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
- const fields = ES.ToTemporalYearMonthRecord(this);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const fields = ES.ToTemporalYearMonthFields(this, fieldNames);
const Date = GetIntrinsic('%Temporal.Date%');
return calendar.dateFromFields({ ...fields, day }, { overflow: 'reject' }, Date);
}
getFields() {
- const fields = ES.ToTemporalYearMonthRecord(this);
- if (!fields) throw new TypeError('invalid receiver');
- fields.calendar = GetSlot(this, CALENDAR);
+ if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
+ const calendar = GetSlot(this, CALENDAR);
+ const fieldNames = ES.CalendarFields(calendar, ['era', 'month', 'year']);
+ const fields = ES.ToTemporalYearMonthFields(this, fieldNames);
+ fields.calendar = calendar;
return fields;
}
getISOFields() {
diff --git a/polyfill/test/usercalendar.mjs b/polyfill/test/usercalendar.mjs
index 5bbcfae5f6..edffa1c3ae 100644
--- a/polyfill/test/usercalendar.mjs
+++ b/polyfill/test/usercalendar.mjs
@@ -283,6 +283,9 @@ describe('Userland calendar', () => {
},
era() {
return undefined;
+ },
+ fields(fields) {
+ return fields.slice();
}
};
@@ -467,6 +470,108 @@ describe('Userland calendar', () => {
});
});
});
+ describe('calendar with extra fields', () => {
+ // Contrived example of a calendar identical to the ISO calendar except that
+ // months are numbered 1, 2, 3, and each year has four seasons of 3 months
+ // numbered 1, 2, 3, 4.
+ const ISO8601Calendar = Temporal.Calendar.from('iso8601').constructor;
+ class SeasonCalendar extends ISO8601Calendar {
+ constructor() {
+ super('season');
+ Object.defineProperty(Temporal.DateTime.prototype, 'season', {
+ get() {
+ return this.calendar.season(this);
+ },
+ configurable: true
+ });
+ Object.defineProperty(Temporal.Date.prototype, 'season', {
+ get() {
+ return this.calendar.season(this);
+ },
+ configurable: true
+ });
+ Object.defineProperty(Temporal.YearMonth.prototype, 'season', {
+ get() {
+ return this.calendar.season(this);
+ },
+ configurable: true
+ });
+ Object.defineProperty(Temporal.MonthDay.prototype, 'season', {
+ get() {
+ return this.calendar.season(this);
+ },
+ configurable: true
+ });
+ }
+ month(date) {
+ const { isoMonth } = date.getISOFields();
+ return ((isoMonth - 1) % 3) + 1;
+ }
+ season(date) {
+ const { isoMonth } = date.getISOFields();
+ return Math.floor((isoMonth - 1) / 3) + 1;
+ }
+ dateFromFields(fields, options, constructor) {
+ const isoMonth = (fields.season - 1) * 3 + fields.month;
+ return super.dateFromFields({ ...fields, month: isoMonth }, options, constructor);
+ }
+ yearMonthFromFields(fields, options, constructor) {
+ const isoMonth = (fields.season - 1) * 3 + fields.month;
+ return super.yearMonthFromFields({ ...fields, month: isoMonth }, options, constructor);
+ }
+ monthDayFromFields(fields, options, constructor) {
+ const isoMonth = (fields.season - 1) * 3 + fields.month;
+ return super.monthDayFromFields({ ...fields, month: isoMonth }, options, constructor);
+ }
+ fields(fields) {
+ fields = fields.slice();
+ if (fields.includes('month')) fields.push('season');
+ return fields;
+ }
+ }
+ const calendar = new SeasonCalendar();
+ const datetime = new Temporal.DateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar);
+ const date = new Temporal.Date(2019, 9, 15, calendar);
+ const yearmonth = new Temporal.YearMonth(2019, 9, calendar);
+ const monthday = new Temporal.MonthDay(9, 15, calendar);
+ it('property getter works', () => {
+ equal(datetime.season, 3);
+ equal(datetime.month, 3);
+ equal(date.season, 3);
+ equal(date.month, 3);
+ equal(yearmonth.season, 3);
+ equal(yearmonth.month, 3);
+ equal(monthday.season, 3);
+ equal(monthday.month, 3);
+ });
+ it('accepts season in from()', () => {
+ equal(
+ `${Temporal.DateTime.from({ year: 2019, season: 3, month: 3, day: 15, calendar })}`,
+ '2019-09-15T00:00:00[c=season]'
+ );
+ equal(`${Temporal.Date.from({ year: 2019, season: 3, month: 3, day: 15, calendar })}`, '2019-09-15[c=season]');
+ equal(`${Temporal.YearMonth.from({ year: 2019, season: 3, month: 3, calendar })}`, '2019-09-01[c=season]');
+ equal(`${Temporal.MonthDay.from({ season: 3, month: 3, day: 15, calendar })}`, '1972-09-15[c=season]');
+ });
+ it('accepts season in with()', () => {
+ equal(`${datetime.with({ season: 2 })}`, '2019-06-15T00:00:00[c=season]');
+ equal(`${date.with({ season: 2 })}`, '2019-06-15[c=season]');
+ equal(`${yearmonth.with({ season: 2 })}`, '2019-06-01[c=season]');
+ equal(`${monthday.with({ season: 2 })}`, '1972-06-15[c=season]');
+ });
+ it('translates month correctly in with()', () => {
+ equal(`${datetime.with({ month: 2 })}`, '2019-08-15T00:00:00[c=season]');
+ equal(`${date.with({ month: 2 })}`, '2019-08-15[c=season]');
+ equal(`${yearmonth.with({ month: 2 })}`, '2019-08-01[c=season]');
+ equal(`${monthday.with({ month: 2 })}`, '1972-08-15[c=season]');
+ });
+ after(() => {
+ delete Temporal.DateTime.prototype.season;
+ delete Temporal.Date.prototype.season;
+ delete Temporal.YearMonth.prototype.season;
+ delete Temporal.MonthDay.prototype.season;
+ });
+ });
});
import { normalize } from 'path';
diff --git a/spec/abstractops.html b/spec/abstractops.html
index 80015c61e1..605e15b4ad 100644
--- a/spec/abstractops.html
+++ b/spec/abstractops.html
@@ -369,7 +369,8 @@ ToRelativeTemporalObject ( _options_ )
1. Set _calendar_ to ! GetISO8601Calendar().
1. Else,
1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
- 1. Let _fields_ be ? ToTemporalDateTimeRecord(_value_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"nanosecond"*, *"second"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateTimeFields(_value_, _fieldNames_).
1. Let _dateFromFields_ be ? Get(_calendar_, *"dateFromFields"*).
1. Let _dateOptions_ be ! OrdinaryObjectCreate(%Object.prototype%).
1. Let _temporalDate_ be ? Call(_dateFromFields_, _calendar_, « _fields_, _dateOptions_, %Temporal.Date% »).
diff --git a/spec/calendar.html b/spec/calendar.html
index e6276bd5e7..5fee808b62 100644
--- a/spec/calendar.html
+++ b/spec/calendar.html
@@ -45,6 +45,20 @@ CalendarToString ( _calendar_ )
+
+ CalendarFields ( _calendar_, _fieldNames_ )
+
+ The CalendarFields abstract operation transforms a List of String values _fieldNames_ into another List of String values by calling the `fields` method of the given _calendar_ Object, falling back to the intrinsic operation if not present.
+
+
+ 1. Let _fields_ be ? Get(_calendar_, *"fields"*).
+ 1. If _fields_ is *undefined*, set _fields_ to %Temporal.Calendar.prototype.fields%.
+ 1. Let _fieldsArray_ be ? CreateArrayFromList(_fieldNames_).
+ 1. Let _arrayLike_ be ? Call(_method_, _calendar_, « _fieldsArray_ »).
+ 1. Return ? CreateListFromArrayLike(_arrayLike_, « String »).
+
+
+
CalendarDateDifference ( _calendar_, _one_, _two_, _options_ )
@@ -421,6 +435,24 @@ Temporal.Calendar.prototype.isLeapYear ( _date_ )
+
+ Temporal.Calendar.prototype.fields ( _fields_ )
+
+ The `fields` method takes one argument _fields_.
+ The following steps are taken:
+
+
+ 1. Let _calendar_ be the *this* value.
+ 1. Perform ? RequireInternalSlot(_calendar_, [[InitializedTemporalCalendar]]).
+ 1. Set _fields_ to ? ToObject(_fields_).
+ 1. Let _fieldNames_ be ? CreateListFromArrayLike(_fields_, « String »).
+ 1. Return ? CreateArrayFromList(_fieldNames_).
+
+
+ This function is the %Temporal.Calendar.prototype.fields% intrinsic object.
+
+
+
Temporal.Calendar.prototype.toString ( )
diff --git a/spec/date.html b/spec/date.html
index b69fb0f7f0..851732f1c2 100644
--- a/spec/date.html
+++ b/spec/date.html
@@ -306,6 +306,8 @@
Temporal.Date.prototype.toYearMonth ( )
1. Let _temporalDate_ be the *this* value.
1. Perform ? RequireInternalSlot(_temporalDate_, [[InitializedTemporalDate]]).
1. Let _calendar_ be _temporalDate_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateFields(_temporalDate_, _fieldNames_).
1. Create via calendar.yearMonthFromFields.
Return ? CreateTemporalYearMonth(_temporalDate_.[[ISOYear]], _temporalDate_.[[ISOMonth]]).
@@ -320,6 +322,8 @@ Temporal.Date.prototype.toMonthDay ( )
1. Let _temporalDate_ be the *this* value.
1. Perform ? RequireInternalSlot(_temporalDate_, [[InitializedTemporalDate]]).
1. Let _calendar_ be _temporalDate_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateFields(_temporalDate_, _fieldNames_).
1. Create via calendar.monthDayFromFields.
Return ? CreateTemporalMonthDay(_temporalDate_.[[ISOMonth]], _temporalDate_.[[ISODay]]).
@@ -332,12 +336,11 @@ Temporal.Date.prototype.getFields ( )
1. Let _temporalDate_ be the *this* value.
- 1. Let _record_ be ? ToPartialDate(_temporalDate_).
- 1. Let _fields_ be ? ObjectCreate(%ObjectPrototype%).
- 1. For each row of , except the header row, in table order, do
- 1. Let _p_ be the Property value of the current row.
- 1. Let _v_ be the value of _record_'s field whose name is the Internal Slot value of the current row.
- 1. Perform ! CreateDataPropertyOrThrow(_fields_, _p_, _v_).
+ 1. Perform ? RequireInternalSlot(_temporalDate_, [[InitializedTemporalDate]]).
+ 1. Let _calendar_ be _temporalDate_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateFields(_temporalDate_, _fieldNames_).
+ 1. Perform ! CreateDataPropertyOrThrow(_fields_, *"calendar"*, _calendar_).
1. Return _fields_.
@@ -411,7 +414,9 @@ Temporal.Date.prototype.with ( _temporalDateLike_ [ , _options_ ] )
1. If Type(_temporalDateLike_) is not Object, then
1. Let _isoString_ be ? ToString(_temporalDateLike_).
1. Set _temporalDateLike_ to ? RelevantTemporalObjectFromString(_isoString_).
- 1. Let _partialDate_ be ? ToPartialDate(_temporalDateLike_).
+ 1. Let _calendar_ be _temporalDate_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _partialDate_ be ? ToPartialDate(_temporalDateLike_, _fieldNames_).
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
1. TODO: Create via calendar.dateFromFields.
@@ -658,10 +663,14 @@ ToTemporalDate ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
1. If Type(_item_) is Object, then
1. If _item_ has an [[InitializedTemporalDate]] internal slot, then
1. Return _item_.
- 1. Let _result_ be ? ToTemporalDateRecord(_item_).
- 1. Else,
- 1. Let _string_ be ? ToString(_item_).
- 1. Let _result_ be ? ParseTemporalDateString(_string_).
+ 1. Let _calendar_ be ? Get(_item_, *"calendar"*).
+ 1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
+ 1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _result_ be ? ToTemporalDateFields(_item_, _fieldNames_).
+ 1. Return TODO: create via calendar.dateFromFields.
+ 1. Let _string_ be ? ToString(_item_).
+ 1. Let _result_ be ? ParseTemporalDateString(_string_).
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
@@ -726,76 +735,40 @@ DifferenceDate ( _y1_, _m1_, _d1_, _y2_, _m2_, _d2_, _largestUnit_ )
-
- ToTemporalDateRecord ( _temporalDateLike_ )
+
+ ToTemporalDateFields ( _temporalDateLike_, _fieldNames_ )
1. Assert: Type(_temporalDateLike_) is Object.
- 1. If _temporalDateLike_ has an [[InitializedTemporalDate]] internal slot, then
- 1. Return the Record {
- [[Year]]: _temporalDateLike_.[[Year]],
- [[Month]]: _temporalDateLike_.[[Month]],
- [[Day]]: _temporalDateLike_.[[Day]],
- }.
- 1. Let _result_ be a new Record with all the internal slots given in the Internal Slot column in .
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
+ 1. For each value _property_ of _fieldNames_, do
1. Let _value_ be ? Get(_temporalDateLike_, _property_).
- 1. If _value_ is *undefined*, then
+ 1. If _property_ is one of *"day"*, *"month"*, or *"year"*, and _value_ is *undefined*, then
1. Throw a *TypeError* exception.
- 1. Let _value_ be ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. If _property_ is not *"era"*, then
+ 1. Let _value_ be ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. Return _result_.
- ToPartialDate ( _temporalDateLike_ )
+ ToPartialDate ( _temporalDateLike_, _fieldNames_ )
1. If Type(_temporalDateLike_) is not Object, then
1. Throw a *TypeError* exception.
- 1. Let _result_ be a new Record with all the internal slots given in the Internal Slot column in , all set to *undefined*.
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
1. Let _any_ be *false*.
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
+ 1. For each value _property_ of _fieldNames_, do
1. Let _value_ be ? Get(_temporalDateLike_, _property_).
1. If _value_ is not *undefined*, then
1. Set _any_ to *true*.
- 1. Set _value_ to ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. If _property_ is not *"era"*, then
+ 1. Set _value_ to ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. If _any_ is *false*, then
1. Throw a *TypeError* exception.
1. Return _result_.
-
-
- Properties of a TemporalDateLike
-
-
-
- Internal Slot |
- Property |
-
-
-
-
- [[Day]] |
- `"day"` |
-
-
- [[Month]] |
- `"month"` |
-
-
- [[Year]] |
- `"year"` |
-
-
- [[Calendar]] |
- `"calendar"` |
-
-
-
-
diff --git a/spec/datetime.html b/spec/datetime.html
index 338b83853a..17b5a9d362 100644
--- a/spec/datetime.html
+++ b/spec/datetime.html
@@ -392,9 +392,12 @@ Temporal.DateTime.prototype.with ( _temporalDateTimeLike_ [ , _options_ ] )<
1. If Type(_temporalDateTimeLike_) is not Object, then
1. Let _isoString_ be ? ToString(_temporalDateTimeLike_).
1. Set _temporalDateTimeLike_ to ? RelevantTemporalObjectFromString(_isoString_).
- 1. Let _partialDateTime_ be ? ToPartialDateTime(_temporalDateTimeLike_).
+ 1. Let _calendar_ be _dateTime_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"nanosecond"*, *"second"*, *"year"* »).
+ 1. Let _partialDateTime_ be ? ToPartialDateTime(_temporalDateTimeLike_, _fieldNames_).
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ 1. Let _fields_ be ? ToTemporalDateTimeFields(_datetime_, _fieldNames_).
1. Let _temporalDate_ be TODO: create via calendar.dateFromFields.
1. Let _year_ be _temporalDate_.[[ISOYear]].
1. Let _month_ be _temporalDate_.[[ISOMonth]].
@@ -647,6 +650,8 @@ Temporal.DateTime.prototype.toYearMonth ( )
1. Let _dateTime_ be the *this* value.
1. Perform ? RequireInternalSlot(_dateTime_, [[InitializedTemporalDateTime]]).
1. Let _calendar_ be _dateTime_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateFields(_dateTime_, _fieldNames_).
1. Create via calendar.yearMonthFromFields.
Return ? CreateTemporalYearMonth(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]]).
@@ -661,6 +666,8 @@ Temporal.DateTime.prototype.toMonthDay ( )
1. Let _dateTime_ be the *this* value.
1. Perform ? RequireInternalSlot(_dateTime_, [[InitializedTemporalDateTime]]).
1. Let _calendar_ be _dateTime_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateFields(_dateTime_, _fieldNames_).
1. Create via calendar.monthDayFromFields.
Return ? CreateTemporalMonthDay(_dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]]).
@@ -685,12 +692,11 @@ Temporal.DateTime.prototype.getFields ( )
1. Let _dateTime_ be the *this* value.
- 1. Let _record_ be ? ToPartialDateTime(_dateTime_).
- 1. Let _fields_ be ? ObjectCreate(%ObjectPrototype%).
- 1. For each row of , except the header row, in table order, do
- 1. Let _p_ be the Property value of the current row.
- 1. Let _v_ be the value of _record_'s field whose name is the Internal Slot value of the current row.
- 1. Perform ! CreateDataPropertyOrThrow(_fields_, _p_, _v_).
+ 1. Perform ? RequireInternalSlot(_dateTime_, [[InitializedTemporalDateTime]]).
+ 1. Let _calendar_ be _dateTime_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"nanosecond"*, *"second"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalDateTimeFields(_dateTime_, _fieldNames_).
+ 1. Perform ! CreateDataPropertyOrThrow(_fields_, *"calendar"*, _calendar_).
1. Return _fields_.
@@ -877,7 +883,11 @@ ToTemporalDateTime ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
1. If Type(_item_) is Object, then
1. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
1. Return _item_.
- 1. Let _result_ be ? ToTemporalDateTimeRecord(_item_).
+ 1. Let _calendar_ be ? Get(_item_, *"calendar"*).
+ 1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
+ 1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"era"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"nanosecond"*, *"second"*, *"year"* »).
+ 1. Let _result_ be ? ToTemporalDateTimeFields(_item_, _fieldNames_).
1. Else,
1. Let _string_ be ? ToString(_item_).
1. Let _result_ be ? ParseTemporalDateTimeString(_string_).
@@ -890,101 +900,30 @@ ToTemporalDateTime ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
- ToPartialDateTime ( _temporalDateTimeLike_ )
+ ToPartialDateTime ( _temporalDateTimeLike_, _fieldNames_ )
1. If Type(_temporalDateTimeLike_) is not Object, then
1. Throw a *TypeError* exception.
- 1. Let _result_ be the new Record {
- [[Year]]: *undefined*,
- [[Month]]: *undefined*,
- [[Day]]: *undefined*,
- [[Hour]]: *undefined*,
- [[Minute]]: *undefined*,
- [[Second]]: *undefined*,
- [[Millisecond]]: *undefined*,
- [[Microsecond]]: *undefined*,
- [[Nanosecond]]: *undefined*
- }.
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
1. Let _any_ be *false*.
- 1. For each row of , except the header row, in table order, do
+ 1. For each value _property_ of _fieldNames_, do
+ 1. Let _value_ be ? Get(_temporalDateTimeLike_, _property_).
+ 1. If _value_ is not *undefined*, then
+ 1. Set _any_ to *true*.
+ 1. If _property_ is not *"era"*, then
+ 1. Set _value_ to ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+ 1. For each row of , except the header row, in table order, do
1. Let _property_ be the Property value of the current row.
1. Let _value_ be ? Get(_temporalDateTimeLike_, _property_).
1. If _value_ is not *undefined*, then
1. Set _any_ to *true*.
1. Set _value_ to ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. If _any_ is *false*, then
1. Throw a *TypeError* exception.
1. Return _result_.
-
-
- Internal slots and properties for Temporal.DateTime argument objects
-
-
-
- Internal Slot |
- Property |
- Optional |
-
-
-
-
- [[Day]] |
- `"day"` |
- *false* |
-
-
-
- [[Hour]] |
- `"hour"` |
- *true* |
-
-
-
- [[Microsecond]] |
- `"microsecond"` |
- *true* |
-
-
-
- [[Millisecond]] |
- `"millisecond"` |
- *true* |
-
-
-
- [[Minute]] |
- `"minute"` |
- *true* |
-
-
-
- [[Month]] |
- `"month"` |
- *false* |
-
-
-
- [[Nanosecond]] |
- `"nanosecond"` |
- *true* |
-
-
-
- [[Second]] |
- `"second"` |
- *true* |
-
-
-
- [[Year]] |
- `"year"` |
- *false* |
-
-
-
-
@@ -1112,31 +1051,26 @@ CreateTemporalDateTimeFromStatic ( _constructor_, _year_, _month_, _day_, _h
-
- ToTemporalDateTimeRecord ( _temporalDateTimeLike_ )
+
+ ToTemporalDateTimeFields ( _temporalDateTimeLike_, _fieldNames_ )
The value of ? ToInteger(*undefined*) is 0.
1. Assert: Type(_temporalDateTimeLike_) is Object.
- 1. If _temporalDateTimeLike_ has an [[InitializedTemporalDateTime]] internal slot, then
- 1. Return the Record {
- [[Year]]: _temporalDateTimeLike_.[[Year]],
- [[Month]]: _temporalDateTimeLike_.[[Month]],
- [[Day]]: _temporalDateTimeLike_.[[Day]],
- [[Hour]]: _temporalDateTimeLike_.[[Hour]],
- [[Minute]]: _temporalDateTimeLike_.[[Minute]],
- [[Second]]: _temporalDateTimeLike_.[[Second]],
- [[Millisecond]]: _temporalDateTimeLike_.[[Millisecond]],
- [[Microsecond]]: _temporalDateTimeLike_.[[Microsecond]],
- [[Nanosecond]]: _temporalDateTimeLike_.[[Nanosecond]]
- }.
- 1. Let _result_ be a new Record with all the internal slots given in the Internal Slot column in .
- 1. For each row of , except the header row, in table order, do
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
+ 1. For each value _property_ of _fieldNames_, do
+ 1. Let _value_ be ? Get(_temporalDateLike_, _property_).
+ 1. If _property_ is one of *"day"*, *"month"*, or *"year"*, and _value_ is *undefined*, then
+ 1. Throw a *TypeError* exception.
+ 1. If _property_ is not *"era"*, then
+ 1. Let _value_ be ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+ 1. For each row of , except the header row, in table order, do
1. Let _property_ be the Property value of the current row.
1. Let _value_ be ? Get(_temporalDateTimeLike_, _property_).
1. If _value_ is *undefined* and the Optional value of the current row is *false*, then
1. Throw a *TypeError* exception.
1. Let _value_ be ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. Return _result_.
diff --git a/spec/monthday.html b/spec/monthday.html
index d321880dfd..ac27ec3648 100644
--- a/spec/monthday.html
+++ b/spec/monthday.html
@@ -142,7 +142,9 @@ Temporal.MonthDay.prototype.with ( _temporalMonthDayLike_ [ , _options_ ] )<
1. Let _monthDay_ be the *this* value.
1. Perform ? RequireInternalSlot(_monthDay_, [[InitializedTemporalMonthDay]]).
- 1. Let _partialMonthDay_ be ? ToPartialMonthDay(_temporalMonthDayLike_).
+ 1. Let _calendar_ be _monthDay_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"* »).
+ 1. Let _partialMonthDay_ be ? ToPartialMonthDay(_temporalMonthDayLike_, _fieldNames_).
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
1. TODO: create via calendar.monthDayFromFields.
@@ -233,8 +235,14 @@ Temporal.MonthDay.prototype.toDateInYear ( _item_ [ , _options_ ] )
1. If _y_ is *undefined*, then
1. Throw a *TypeError* exception.
1. Let _y_ be ? ToInteger(_y_).
+ 1. Let _era_ be ? Get(_item_, *"era"*).
1. Else,
1. Let _y_ be ? ToInteger(_item_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"* »).
+ 1. Let _fields_ be ? ToTemporalMonthDayFields(_monthDay_, _fieldNames_).
+ 1. Perform ! Set(_fields_, *"year"*, _y_, *false*).
+ 1. If _era_ is not *undefined*, then
+ 1. Perform ! Set(_fields_, *"era"*, _era_, *false*).
1. TODO: create via calendar.dateFromFields.
Return ? CreateTemporalDate(_date_.[[Year]], _date_.[[Month]], _date_.[[Day]]).
@@ -247,12 +255,11 @@ Temporal.MonthDay.prototype.getFields ( )
1. Let _monthDay_ be the *this* value.
- 1. Let _record_ be ? ToPartialMonthDay(_monthDay_).
- 1. Let _fields_ be ? ObjectCreate(%ObjectPrototype%).
- 1. For each row of , except the header row, in table order, do
- 1. Let _p_ be the Property value of the current row.
- 1. Let _v_ be the value of _record_'s field whose name is the Internal Slot value of the current row.
- 1. Perform ! CreateDataPropertyOrThrow(_fields_, _p_, _v_).
+ 1. Perform ? RequireInternalSlot(_monthDay_, [[InitializedTemporalMonthDay]]).
+ 1. Let _calendar_ be _monthDay_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"* »).
+ 1. Let _fields_ be ? ToTemporalMonthDayFields(_monthDay_, _fieldNames_).
+ 1. Perform ! CreateDataPropertyOrThrow(_fields_, *"calendar"*, _calendar_).
1. Return _fields_.
@@ -353,12 +360,16 @@ ToTemporalMonthDay ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
1. If Type(_item_) is Object, then
1. If _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
1. Return _item_.
- 1. Let _result_ be ? ToTemporalMonthDayRecord(_item_).
- 1. Let _referenceISOYear_ be _result_.[[Year]].
- 1. Else,
- 1. Let _string_ be ? ToString(_item_).
- 1. Let _result_ be ? ParseTemporalMonthDayString(_string_).
- 1. Let _referenceISOYear_ be the first leap year after the Unix epoch (1972).
+ 1. Let _calendar_ be ? Get(_item_, *"calendar"*).
+ 1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
+ 1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"* »).
+ 1. Let _result_ be ? ToTemporalMonthDayFields(_item_, _fieldNames_).
+ 1. Return TODO: create via calendar.monthDayFromFields.
+ 1. Let _string_ be ? ToString(_item_).
+ 1. Let _result_ be ? ParseTemporalMonthDayString(_string_).
+ 1. Let _referenceISOYear_ be the first leap year after the Unix epoch (1972).
+ 1. TODO: get referenceISOYear from string.
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
@@ -368,48 +379,22 @@ ToTemporalMonthDay ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
- ToPartialMonthDay ( _temporalMonthDayLike_ )
+ ToPartialMonthDay ( _temporalMonthDayLike_, _fieldNames_ )
1. If Type(_temporalMonthDayLike_) is not Object, then
1. Throw a *TypeError* exception.
- 1. Let _result_ be the new Record {
- [[Month]]: *undefined*,
- [[Day]]: *undefined*
- }.
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
1. Let _any_ be *false*.
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
+ 1. For each value _property_ of _fieldNames_, do
1. Let _value_ be ? Get(_temporalMonthDayLike_, _property_).
1. If _value_ is not *undefined*, then
1. Set _any_ to *true*.
1. Set _value_ to ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. If _any_ is *false*, then
1. Throw a *TypeError* exception.
1. Return _result_.
-
-
- Properties of a TemporalMonthDayLike
-
-
-
- Internal Slot |
- Property |
-
-
-
-
- [[Day]] |
- `"day"` |
-
-
- [[Month]] |
- `"month"` |
-
-
-
-
@@ -502,25 +487,19 @@ CreateTemporalMonthDayFromStatic ( _constructor_, _month_, _day_, _reference
-
- ToTemporalMonthDayRecord ( _temporalMonthDayLike_ )
+
+ ToTemporalMonthDayFields ( _temporalMonthDayLike_, _fieldNames_ )
1. Assert: Type(_temporalMonthDayLike_) is Object.
- 1. If _temporalMonthDayLike_ has an [[InitializedTemporalMonthDay]] internal slot, then
- 1. Return the Record {
- [[Month]]: _temporalMonthDayLike_.[[ISOMonth]],
- [[Day]]: _temporalMonthDayLike_.[[ISODay]],
- [[Year]]: _temporalMonthDayLike_.[[ISOYear]]
- }.
- 1. Let _result_ be a new Record with all the internal slots given in the Internal Slot column in , as well as a [[Year]] slot.
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
- 1. Let _value_ be ? Get(_temporalMonthDayLike_, _property_).
- 1. If _value_ is *undefined*, then
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
+ 1. For each value _property_ of _fieldNames_, do
+ 1. Let _value_ be ? Get(_temporalDateLike_, _property_).
+ 1. If _property_ is one of *"month"* or *"year"*, and _value_ is *undefined*, then
1. Throw a *TypeError* exception.
1. Let _value_ be ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
- 1. Set _result_.[[Year]] to the first leap year after the Unix epoch (1972).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+ 1. Let _year_ be the first leap year after the Unix epoch (1972).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, *"year"*, _year_).
1. Return _result_.
diff --git a/spec/yearmonth.html b/spec/yearmonth.html
index 7614dc254e..1901f8af64 100644
--- a/spec/yearmonth.html
+++ b/spec/yearmonth.html
@@ -208,7 +208,9 @@ Temporal.YearMonth.prototype.with ( _temporalYearMonthLike_ [ , _options_ ]
1. Let _yearMonth_ be the *this* value.
1. Perform ? RequireInternalSlot(_yearMonth_, [[InitializedTemporalYearMonth]]).
- 1. Let _partialYearMonth_ be ? ToPartialYearMonth(_temporalYearMonthLike_).
+ 1. Let _calendar_ be _yearMonth_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"era"*, *"month"*, *"year"* »).
+ 1. Let _partialYearMonth_ be ? ToPartialYearMonth(_temporalYearMonthLike_, _fieldNames_).
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
1. TODO: Create via calendar.dateFromFields.
@@ -377,7 +379,11 @@ Temporal.YearMonth.prototype.toDateOnDay ( _day_ )
1. Let _yearMonth_ be the *this* value.
1. Perform ? RequireInternalSlot(_yearMonth_, [[InitializedTemporalYearMonth]]).
+ 1. Let _calendar_ be _yearMonth_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalYearMonthFields(_yearMonth_, _fieldNames_).
1. Let _d_ be ? ToInteger(_day_).
+ 1. Perform ! Set(_fields_, *"day"*, _d_, *false*).
1. TODO: create via calendar.dateFromFields.
Return ? CreateTemporalDate(_yearMonth_.[[ISOYear]], _yearMonth_.[[ISOMonth]], _d_).
@@ -390,12 +396,11 @@ Temporal.YearMonth.prototype.getFields ( )
1. Let _yearMonth_ be the *this* value.
- 1. Let _record_ be ? ToPartialYearMonth(_yearMonth_).
- 1. Let _fields_ be ? ObjectCreate(%ObjectPrototype%).
- 1. For each row of , except the header row, in table order, do
- 1. Let _p_ be the Property value of the current row.
- 1. Let _v_ be the value of _record_'s field whose name is the Internal Slot value of the current row.
- 1. Perform ! CreateDataPropertyOrThrow(_fields_, _p_, _v_).
+ 1. Perform ? RequireInternalSlot(_yearMonth_, [[InitializedTemporalYearMonth]]).
+ 1. Let _calendar_ be _yearMonth_.[[Calendar]].
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"era"*, *"month"*, *"year"* »).
+ 1. Let _fields_ be ? ToTemporalYearMonthFields(_yearMonth_, _fieldNames_).
+ 1. Perform ! CreateDataPropertyOrThrow(_fields_, *"calendar"*, _calendar_).
1. Return _fields_.
@@ -496,12 +501,16 @@ ToTemporalYearMonth ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
1. If Type(_item_) is Object, then
1. If _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
1. Return _item_.
- 1. Let _result_ be ? ToTemporalYearMonthRecord(_item_).
- 1. Let _referenceISODay_ be _result_.[[Day]].
- 1. Else,
- 1. Let _string_ be ? ToString(_item_).
- 1. Let _result_ be ? ParseTemporalYearMonthString(_string_).
- 1. Let _referenceISODay_ be 1.
+ 1. Let _calendar_ be ? Get(_item_, *"calendar"*).
+ 1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
+ 1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"era"*, *"month"*, *"year"* »).
+ 1. Let _result_ be ? ToTemporalYearMonthFields(_item_, _fieldNames_).
+ 1. Return TODO: create via calendar.yearMonthFromFields.
+ 1. Let _string_ be ? ToString(_item_).
+ 1. Let _result_ be ? ParseTemporalYearMonthString(_string_).
+ 1. Let _referenceISODay_ be 1.
+ 1. TODO: get referenceISODay from string.
1. Let _calendar_ be _result_.[[Calendar]].
1. If _calendar_ is *undefined*, set _calendar_ to ! GetISO8601Calendar().
1. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
@@ -511,49 +520,25 @@ ToTemporalYearMonth ( _item_ [ , _constructor_ [ , _overflow_ ] ] )
- ToPartialYearMonth ( _temporalYearMonthLike_ )
+ ToPartialYearMonth ( _temporalYearMonthLike_, _fieldNames_ )
1. If Type(_temporalYearMonthLike_) is not Object, then
1. Throw a *TypeError* exception.
- 1. Let _result_ be the new Record {
- [[Year]]: *undefined*,
- [[Month]]: *undefined*
- }.
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
1. Let _any_ be *false*.
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
+ 1. For each value _property_ of _fieldNames_, do
1. Let _value_ be ? Get(_temporalYearMonthLike_, _property_).
1. If _value_ is not *undefined*, then
1. Set _any_ to *true*.
- 1. Set _value_ to ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
+ 1. If _property_ is not *"era"*, then
+ 1. Set _value_ to ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
1. If _any_ is *false*, then
1. Throw a *TypeError* exception.
1. Return _result_.
-
-
- Properties of a TemporalYearMonthLike
-
-
-
- Internal Slot |
- Property |
-
-
-
-
- [[Month]] |
- `"month"` |
-
-
- [[Year]] |
- `"year"` |
-
-
-
-
+
RegulateYearMonth ( _year_, _month_, _overflow_ )
@@ -663,25 +648,19 @@ CreateTemporalYearMonthFromStatic ( _constructor_, _year_, _month_, _referen
-
- ToTemporalYearMonthRecord ( _temporalYearMonthLike_ )
+
+ ToTemporalYearMonthFields ( _temporalYearMonthLike_, _fieldNames_ )
1. Assert: Type(_temporalYearMonthLike_) is Object.
- 1. If _temporalYearMonthLike_ has an [[InitializedTemporalYearMonth]] internal slot, then
- 1. Return the Record {
- [[Year]]: _temporalYearMonthLike_.[[ISOYear]],
- [[Month]]: _temporalYearMonthLike_.[[ISOMonth]],
- [[Day]]: _temporalYearMonthLike_.[[ISODay]]
- }.
- 1. Let _result_ be a new Record with all the internal slots given in the Internal Slot column in , as well as a [[Day]] slot.
- 1. For each row of , except the header row, in table order, do
- 1. Let _property_ be the Property value of the current row.
- 1. Let _value_ be ? Get(_temporalYearMonthLike_, _property_).
- 1. If _value_ is *undefined*, then
+ 1. Let _result_ be ? OrdinaryObjectCreate(%Object.prototype%).
+ 1. For each value _property_ of _fieldNames_, do
+ 1. Let _value_ be ? Get(_temporalDateLike_, _property_).
+ 1. If _property_ is one of *"day"*, *"month"*, or *"year"*, and _value_ is *undefined*, then
1. Throw a *TypeError* exception.
- 1. Let _value_ be ? ToInteger(_value_).
- 1. Set _result_'s internal slot whose name is the Internal Slot value of the current row to _value_.
- 1. Set _result_.[[Day]] to 1.
+ 1. If _property_ is not *"era"*, then
+ 1. Let _value_ be ? ToInteger(_value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+ 1. Perform ! CreateDataPropertyOrThrow(_result_, *"day"*, 1).
1. Return _result_.