Skip to content

Commit

Permalink
Normative: Store strings or objects in calendar slot, remove getters
Browse files Browse the repository at this point in the history
Refactor so that Temporal objects' [[Calendar]] internal slot can now
store either a string (builtin calendar) or an object (custom calendar).

The .calendar getter is replaced with a .calendarId getter that always
returns a string, and a .getCalendar() method that always returns a new
object if the slot stored a string.

The operations ToTemporalCalendarIdentifier and ToTemporalCalendarObject
convert a [[Calendar]] slot value to one or the other.

See: #1808
  • Loading branch information
ptomato committed Apr 7, 2023
1 parent 61fbb40 commit 7cf55b9
Show file tree
Hide file tree
Showing 23 changed files with 547 additions and 172 deletions.
7 changes: 4 additions & 3 deletions docs/calendar.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ There are two ways to do this.

The recommended way is to create a class inheriting from `Temporal.Calendar`.
You must use one of the built-in calendars as the "base calendar".
In the class's constructor, call `super()` with the identifier of the base calendar.
In the class's constructor, call `super()` with the identifier of a built-in calendar to serve as a base.
The class must override `toString()` to return its own identifier.
Overriding all the other members is optional.
If you don't override the optional members, then they will behave as in the base calendar.
Overriding all the other properties of `Temporal.Calendar.prototype` is optional.
Any property that's not overridden will behave as in the base calendar.
It's recommended to override `dateFromFields()`, `monthDayFromFields()`, `yearMonthFromFields()`, and `dateAdd()`so that they return Temporal objects with the custom calendar and not the base calendar.

The other, more difficult, way to create a custom calendar is to create a plain object implementing the `Temporal.Calendar` protocol, without subclassing.
The object must implement all of the `Temporal.Calendar` properties and methods except for `id`, `fields()`, `mergeFields()`, and `toJSON()`.
Expand Down
27 changes: 18 additions & 9 deletions docs/plaindate.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ It can also be combined with a `Temporal.PlainTime` to yield a "zoneless" `Tempo

## Constructor

### **new Temporal.PlainDate**(_isoYear_: number, _isoMonth_: number, _isoDay_: number, _calendar_?: string | object) : Temporal.PlainDate
### **new Temporal.PlainDate**(_isoYear_: number, _isoMonth_: number, _isoDay_: number, _calendar_: string | object = "iso8601") : Temporal.PlainDate

**Parameters:**

- `isoYear` (number): A year.
- `isoMonth` (number): A month, ranging between 1 and 12 inclusive.
- `isoDay` (number): A day of the month, ranging between 1 and 31 inclusive.
- `calendar` (optional `Temporal.Calendar`, plain object, or string): A calendar to project the date into.
- `calendar` (optional string, `Temporal.Calendar` instance, or plain object): A calendar to project the date into.

**Returns:** a new `Temporal.PlainDate` object.

Expand All @@ -37,6 +37,9 @@ Together, `isoYear`, `isoMonth`, and `isoDay` must represent a valid date in tha
The range of allowed values for this type is exactly enough that calling [`toPlainDate()`](./plaindatetime.md#toPlainDate) on any valid `Temporal.PlainDateTime` will succeed.
If `isoYear`, `isoMonth`, and `isoDay` form a date outside of this range, then this function will throw a `RangeError`.

Usually `calendar` will be a string containing the identifier of a built-in calendar, such as `'islamic'` or `'gregory'`.
Use an object if you need to supply [custom calendar behaviour](./calendar.md#custom-calendars).

> **NOTE**: The `isoMonth` argument ranges from 1 to 12, which is different from legacy `Date` where months are represented by zero-based indices (0 to 11).
Usage examples:
Expand Down Expand Up @@ -103,10 +106,8 @@ date = Temporal.PlainDate.from(Temporal.PlainDateTime.from('2006-08-24T15:43:27'
// => 2006-08-24
// same as above; Temporal.PlainDateTime has year, month, and day properties

calendar = Temporal.Calendar.from('islamic');
date = Temporal.PlainDate.from({ year: 1427, month: 8, day: 1, calendar }); // => 2006-08-24[u-ca=islamic]
date = Temporal.PlainDate.from({ year: 1427, month: 8, day: 1, calendar: 'islamic' });
// => 2006-08-24[u-ca=islamic] (same as above)
// => 2006-08-24[u-ca=islamic]

// Different overflow modes
date = Temporal.PlainDate.from({ year: 2001, month: 13, day: 1 }, { overflow: 'constrain' });
Expand Down Expand Up @@ -199,9 +200,10 @@ date.day; // => 18
```
<!-- prettier-ignore-end -->

### date.**calendar** : object
### date.**calendarId** : string

The `calendar` read-only property gives the calendar that the `year`, `month`, and `day` properties are interpreted in.
The `calendarId` read-only property gives the identifier of the calendar that the `year`, `month`, `monthCode`, and `day` properties are interpreted in.
If the date was created with a custom calendar object, this gives the `id` property of that object.

### date.**era** : string | undefined

Expand Down Expand Up @@ -805,9 +807,16 @@ date.toPlainYearMonth(); // => 2006-08
date.toPlainMonthDay(); // => 08-24
```

### date.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: object }
### date.**getCalendar**(): object

**Returns:** a `Temporal.Calendar` instance or plain object representing the calendar in which `date` is reckoned.

This method is mainly useful if you need an object on which to call calendar methods.
Most code will not need to use it.

### date.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: string | object }

**Returns:** a plain object with properties expressing `date` in the ISO 8601 calendar, as well as the value of `date.calendar`.
**Returns:** a plain object with properties expressing `date` in the ISO 8601 calendar, as well as the calendar (usually a string, but may be an object) in which `date` is reckoned.

This method is mainly useful if you are implementing a custom calendar.
Most code will not need to use it.
Expand Down
29 changes: 18 additions & 11 deletions docs/plaindatetime.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ To learn more about time zones and DST best practices, visit [Time Zones and Res

## Constructor

### **new Temporal.PlainDateTime**(_isoYear_: number, _isoMonth_: number, _isoDay_: number, _isoHour_: number = 0, _isoMinute_: number = 0, _isoSecond_: number = 0, _isoMillisecond_: number = 0, _isoMicrosecond_: number = 0, _isoNanosecond_: number = 0, _calendar_?: string | object) : Temporal.PlainDateTime
### **new Temporal.PlainDateTime**(_isoYear_: number, _isoMonth_: number, _isoDay_: number, _isoHour_: number = 0, _isoMinute_: number = 0, _isoSecond_: number = 0, _isoMillisecond_: number = 0, _isoMicrosecond_: number = 0, _isoNanosecond_: number = 0, _calendar_: string | object = "iso8601") : Temporal.PlainDateTime

**Parameters:**

Expand All @@ -49,7 +49,7 @@ To learn more about time zones and DST best practices, visit [Time Zones and Res
- `isoMillisecond` (optional number): A number of milliseconds, ranging between 0 and 999 inclusive.
- `isoMicrosecond` (optional number): A number of microseconds, ranging between 0 and 999 inclusive.
- `isoNanosecond` (optional number): A number of nanoseconds, ranging between 0 and 999 inclusive.
- `calendar` (optional `Temporal.Calendar`, plain object, or string): A calendar to project the datetime into.
- `calendar` (optional string, `Temporal.Calendar` instance, or plain object): A calendar to project the datetime into.

**Returns:** a new `Temporal.PlainDateTime` object.

Expand All @@ -65,6 +65,9 @@ Together, `isoYear`, `isoMonth`, and `isoDay` must represent a valid date in tha
The range of allowed values for this type is exactly enough that calling `timeZone.getPlainDateTimeFor(instant)` will succeed when `timeZone` is any built-in `Temporal.TimeZone` and `instant` is any valid `Temporal.Instant`.
If the parameters passed in to this constructor form a date outside of this range, then this function will throw a `RangeError`.

Usually `calendar` will be a string containing the identifier of a built-in calendar, such as `'islamic'` or `'gregory'`.
Use an object if you need to supply [custom calendar behaviour](./calendar.md#custom-calendars).

> **NOTE**: The `isoMonth` argument ranges from 1 to 12, which is different from legacy `Date` where months are represented by zero-based indices (0 to 11).
Usage examples:
Expand Down Expand Up @@ -96,7 +99,7 @@ If the value is any other object, a `Temporal.PlainDateTime` will be constructed
At least the `year` (or `era` and `eraYear`), `month` (or `monthCode`), and `day` properties must be present.
Default values for other missing fields are determined by the calendar.

If the `calendar` property is not present, it's assumed to be `Temporal.Calendar.from('iso8601')`, the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates).
If the `calendar` property is not present, it's assumed to be `'iso8601'` (identifying the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates)).
Any other missing properties will be assumed to be 0 (for time fields).

Any non-object value is converted to a string, which is expected to be in ISO 8601 format.
Expand Down Expand Up @@ -147,12 +150,8 @@ dt = Temporal.PlainDateTime.from(Temporal.PlainDate.from('1995-12-07T03:24:30'))
// => 1995-12-07T00:00:00
// same as above; Temporal.PlainDate has year, month, and day properties

calendar = Temporal.Calendar.from('hebrew');
dt = Temporal.PlainDateTime.from({ year: 5756, month: 3, day: 14, hour: 3, minute: 24, second: 30, calendar });
// => 1995-12-07T03:24:30[u-ca=hebrew]
dt = Temporal.PlainDateTime.from({ year: 5756, month: 3, day: 14, hour: 3, minute: 24, second: 30, calendar: 'hebrew' });
// => 1995-12-07T03:24:30[u-ca=hebrew]
// same as above

// Different overflow modes
dt = Temporal.PlainDateTime.from({ year: 2001, month: 13, day: 1 }, { overflow: 'constrain' });
Expand Down Expand Up @@ -290,9 +289,10 @@ dt.nanosecond; // => 500
```
<!-- prettier-ignore-end -->

### datetime.**calendar** : object
### datetime.**calendarId** : string

The `calendar` read-only property gives the calendar that the `year`, `month`, `day`, `hour`, `minute`, `second`, `millisecond`, `microsecond`, and `nanosecond` properties are interpreted in.
The `calendarId` read-only property gives the identifier of the calendar that the `year`, `month`, `monthCode`, and `day` properties are interpreted in.
If the date was created with a custom calendar object, this gives the `id` property of that object.

### datetime.**era** : string | undefined

Expand Down Expand Up @@ -1053,9 +1053,16 @@ dt.toPlainMonthDay(); // => 12-07
dt.toPlainTime(); // => 03:24:30.0000035
```

### datetime.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, isoHour: number, isoMinute: number, isoSecond: number, isoMillisecond: number, isoMicrosecond: number, isoNanosecond: number, calendar: object }
### datetime.**getCalendar**(): object

**Returns:** a `Temporal.Calendar` instance or plain object representing the calendar in which `datetime` is reckoned.

This method is mainly useful if you need an object on which to call calendar methods.
Most code will not need to use it.

### datetime.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, isoHour: number, isoMinute: number, isoSecond: number, isoMillisecond: number, isoMicrosecond: number, isoNanosecond: number, calendar: string | object }

**Returns:** a plain object with properties expressing `datetime` in the ISO 8601 calendar, as well as the value of `datetime.calendar`.
**Returns:** a plain object with properties expressing `datetime` in the ISO 8601 calendar, as well as the calendar (usually a string, but may be an object) in which `datetime` is reckoned.

This method is mainly useful if you are implementing a custom calendar.
Most code will not need to use it.
Expand Down
24 changes: 18 additions & 6 deletions docs/plainmonthday.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ A `Temporal.PlainMonthDay` can be converted into a `Temporal.PlainDate` by combi

## Constructor

### **new Temporal.PlainMonthDay**(_isoMonth_: number, _isoDay_: number, _calendar_?: string | object, _referenceISOYear_?: number) : Temporal.PlainMonthDay
### **new Temporal.PlainMonthDay**(_isoMonth_: number, _isoDay_: number, _calendar_: string | object = "iso8601", _referenceISOYear_: number = 1972) : Temporal.PlainMonthDay

**Parameters:**

- `isoMonth` (number): A month, ranging between 1 and 12 inclusive.
- `isoDay` (number): A day of the month, ranging between 1 and 31 inclusive.
- `calendar` (optional `Temporal.Calendar`, plain object, or string): A calendar to project the date into.
- `calendar` (optional string, `Temporal.Calendar` instance, or plain object): A calendar to project the date into.
- `referenceISOYear` (optional for ISO 8601 calendar; required for other calendars):
A reference year in the ISO 8601 calendar for disambiguation when implementing calendar systems.
The default for the ISO 8601 calendar is the first leap year after the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time).
Expand All @@ -31,6 +31,10 @@ A `Temporal.PlainMonthDay` can be converted into a `Temporal.PlainDate` by combi
> When creating instances for non-ISO-8601 calendars (except when implementing a custom calendar) use the `from()` method which will automatically set a valid and `equals`-compatible reference year.
All values are given as reckoned in the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates).
Together, `referenceISOYear`, `isoMonth`, and `isoDay` must represent a valid date in that calendar, even if you are passing a different calendar as the `calendar` parameter.

Usually `calendar` will be a string containing the identifier of a built-in calendar, such as `'islamic'` or `'gregory'`.
Use an object if you need to supply [custom calendar behaviour](./calendar.md#custom-calendars).

The `referenceISOYear` ensures that month/day combinations like February 29 (a leap day in the ISO 8601 calendar) or 15 Adar I (in a leap month in the Hebrew calendar) can be used for `Temporal.PlainMonthDay`, even though those dates don't occur every calendar year.
`referenceISOYear` corresponds to a calendar year where this month and day actually exist.
Expand Down Expand Up @@ -162,9 +166,10 @@ md.month; // => undefined
// (no `month` property; use `monthCode` instead)
```

### monthDay.**calendar** : object
### monthDay.**calendarId** : object

The `calendar` read-only property gives the calendar that the `monthCode` and `day` properties are interpreted in.
The `calendarId` read-only property gives the calendar that the `monthCode` and `day` properties are interpreted in.
If `monthDay` was created with a custom calendar object, this gives the `id` property of that object.

## Methods

Expand Down Expand Up @@ -384,9 +389,16 @@ md = Temporal.PlainMonthDay.from({
date = md.toPlainDate({ era: 'reiwa', eraYear: 2 }); // => 2020-01-01[u-ca=japanese]
```

### monthDay.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: object }
### monthDay.**getCalendar**(): object

**Returns:** a `Temporal.Calendar` instance or plain object representing the calendar in which `monthDay` is reckoned.

This method is mainly useful if you need an object on which to call calendar methods.
Most code will not need to use it.

### monthDay.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: string | object }

**Returns:** a plain object with properties expressing `monthDay` in the ISO 8601 calendar, as well as the value of `monthDay.calendar`.
**Returns:** a plain object with properties expressing `monthDay` in the ISO 8601 calendar, as well as the calendar (usually a string, but may be an object) in which `monthDay` is reckoned.

This method is mainly useful if you are implementing a custom calendar.
Most code will not need to use it.
Expand Down
26 changes: 19 additions & 7 deletions docs/plainyearmonth.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ A `Temporal.PlainYearMonth` can be converted into a `Temporal.PlainDate` by comb

## Constructor

### **new Temporal.PlainYearMonth**(_isoYear_: number, _isoMonth_: number, _calendar_?: string | object, _referenceISODay_: number = 1) : Temporal.PlainYearMonth
### **new Temporal.PlainYearMonth**(_isoYear_: number, _isoMonth_: number, _calendar_: string | object = "iso8601", _referenceISODay_: number = 1) : Temporal.PlainYearMonth

**Parameters:**

- `isoYear` (number): A year.
- `isoMonth` (number): A month, ranging between 1 and 12 inclusive.
- `calendar` (optional `Temporal.Calendar`, plain object, or string): A calendar to project the month into.
- `calendar` (optional string, `Temporal.Calendar` instance, or plain object): A calendar to project the month into.
- `referenceISODay` (optional for ISO 8601 calendar; required for other calendars): A reference day, used for disambiguation when implementing calendar systems.
For the ISO 8601 calendar, this parameter will default to 1 if omitted.
For other calendars, the must set this parameter to the ISO-calendar day corresponding to the first day of the desired calendar year and month.
Expand All @@ -35,10 +35,14 @@ A `Temporal.PlainYearMonth` can be converted into a `Temporal.PlainDate` by comb
**Returns:** a new `Temporal.PlainYearMonth` object.

All values are given as reckoned in the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates).
Together, `isoYear`, `isoMonth`, and `referenceISODay` must represent a valid date in that calendar, even if you are passing a different calendar as the `calendar` parameter.

The range of allowed values for this type is exactly enough that calling [`toPlainYearMonth()`](./plaindate.md#toPlainYearMonth) on any valid `Temporal.PlainDate` will succeed.
If `isoYear` and `isoMonth` are outside of this range, then this function will throw a `RangeError`.

Usually `calendar` will be a string containing the identifier of a built-in calendar, such as `'islamic'` or `'gregory'`.
Use an object if you need to supply [custom calendar behaviour](./calendar.md#custom-calendars).

> **NOTE**: The `isoMonth` argument ranges from 1 to 12, which is different from legacy `Date` where months are represented by zero-based indices (0 to 11).
Usage examples:
Expand Down Expand Up @@ -69,7 +73,7 @@ If the value is another `Temporal.PlainYearMonth` object, a new object represent
If the value is any other object, it must have `year` (or `era` and `eraYear`), `month` (or `monthCode`) properties, and optionally a `calendar` property.
A `Temporal.PlainYearMonth` will be constructed from these properties.

If the `calendar` property is not present, it's assumed to be `Temporal.Calendar.from('iso8601')`, the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates).
If the `calendar` property is not present, it's assumed to be `'iso8601'` (identifying the [ISO 8601 calendar](https://en.wikipedia.org/wiki/ISO_8601#Dates)).
In this calendar, `era` is ignored.

Any non-object value is converted to a string, which is expected to be in ISO 8601 format.
Expand Down Expand Up @@ -185,9 +189,10 @@ ym.month; // => 6
ym.monthCode; // => 'M05L'
```

### yearMonth.**calendar** : object
### yearMonth.**calendarId** : object

The `calendar` read-only property gives the calendar that the `year` and `month` properties are interpreted in.
The `calendarId` read-only property gives the identifier of the calendar that the `year`, `month`, and `monthCode` properties are interpreted in.
If `yearMonth` was created with a custom calendar object, this gives the `id` property of that project.

### yearMonth.**era** : string | undefined

Expand Down Expand Up @@ -632,9 +637,16 @@ ym = Temporal.PlainYearMonth.from('2019-06');
ym.toPlainDate({ day: 24 }); // => 2019-06-24
```

### yearMonth.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: object }
### yearMonth.**getCalendar**(): object

**Returns:** a `Temporal.Calendar` instance or plain object representing the calendar in which `yearMonth` is reckoned.

This method is mainly useful if you need an object on which to call calendar methods.
Most code will not need to use it.

### yearMonth.**getISOFields**(): { isoYear: number, isoMonth: number, isoDay: number, calendar: string | object }

**Returns:** a plain object with properties expressing `yearMonth` in the ISO 8601 calendar, as well as the value of `yearMonth.calendar`.
**Returns:** a plain object with properties expressing `yearMonth` in the ISO 8601 calendar, as well as the calendar (usually a string, but may be an object) that `yearMonth` is reckoned in.

This method is mainly useful if you are implementing a custom calendar.
Most code will not need to use it.
Expand Down
Loading

0 comments on commit 7cf55b9

Please sign in to comment.