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 SlotProperty
[[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 SlotPropertyOptional
[[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 SlotProperty
[[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 SlotProperty
[[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_.