diff --git a/docs/cookbook/stockExchangeTimeZone.mjs b/docs/cookbook/stockExchangeTimeZone.mjs index 34a47e0e97..f780140d96 100644 --- a/docs/cookbook/stockExchangeTimeZone.mjs +++ b/docs/cookbook/stockExchangeTimeZone.mjs @@ -80,6 +80,7 @@ function getPreviousMarketClose(instant) { } class NYSETimeZone extends Temporal.TimeZone { + #id = 'NYSE'; constructor() { super('America/New_York'); } @@ -124,8 +125,11 @@ class NYSETimeZone extends Temporal.TimeZone { const ns = zdt.offsetNanoseconds + zdt.until(zdtWhenMarketIsOpen, { largestUnit: 'nanosecond' }).nanoseconds; return ns; } + get id() { + return this.#id; + } toString() { - return 'NYSE'; + return this.#id; } } diff --git a/docs/timezone.md b/docs/timezone.md index c1dbf58447..628efb67de 100644 --- a/docs/timezone.md +++ b/docs/timezone.md @@ -20,15 +20,15 @@ There are two ways to do this. The recommended way is to create a class inheriting from `Temporal.TimeZone`. You must use one of the built-in time zones as the "base time zone". In the class's constructor, call `super()` with the identifier of a built-in time zone to serve as a base. -The class must override `toString()` to return its own identifier. +The class must override the `id` prototype property, and should override `toString()` and `toJSON()` to match. Overriding all the other properties of `Temporal.TimeZone.prototype` is optional. Any property that is not overridden will behave as in the base time zone. The other, more difficult, way to create a custom time zone is to create a plain object implementing the `Temporal.TimeZone` protocol, without subclassing. -The object must have at least `getOffsetNanosecondsFor()`, `getPossibleInstantsFor()`, and `toString()` methods. +The object must have at least `getOffsetNanosecondsFor()` and `getPossibleInstantsFor()` methods, and an `id` property. Any object with those three methods will return the correct output from any Temporal property or method. However, most other code will assume that custom time zones act like built-in `Temporal.TimeZone` objects. -To interoperate with libraries or other code that you didn't write, then you should implement all the other `Temporal.TimeZone` members as well: `id`, `getOffsetStringFor()`, `getPlainDateTimeFor()`, `getInstantFor()`, `getNextTransition()`, `getPreviousTransition()`, and `toJSON()`. +To interoperate with libraries or other code that you didn't write, then you should implement all the other `Temporal.TimeZone` members as well: `toString()`, `toJSON()`, `getOffsetStringFor()`, `getPlainDateTimeFor()`, `getInstantFor()`, `getNextTransition()`, `getPreviousTransition()`, and `toJSON()`. Your object must not have a `timeZone` property, so that it can be distinguished in `Temporal.TimeZone.from()` from other Temporal objects that have a time zone. The identifier of a custom time zone must consist of one or more components separated by slashes (`/`), as described in the [tzdata documentation](https://htmlpreview.github.io/?https://github.com/eggert/tz/blob/master/theory.html#naming). @@ -136,7 +136,7 @@ tz2 = Temporal.TimeZone.from(tz); The `id` property gives an unambiguous identifier for the time zone. Effectively, this is the canonicalized version of whatever `timeZoneIdentifier` was passed as a parameter to the constructor. -When subclassing `Temporal.TimeZone`, this property doesn't need to be overridden because the default implementation gives the result of calling `toString()`. +When subclassing `Temporal.TimeZone`, this property must be overridden to provide an identifier for the custom time zone. ## Methods diff --git a/polyfill/index.d.ts b/polyfill/index.d.ts index 091d573bd7..98c4fa6115 100644 --- a/polyfill/index.d.ts +++ b/polyfill/index.d.ts @@ -1090,7 +1090,7 @@ export namespace Temporal { * A plain object implementing the protocol for a custom time zone. */ export interface TimeZoneProtocol { - id?: string; + id: string; timeZone?: never; getOffsetNanosecondsFor(instant: Temporal.Instant | string): number; getOffsetStringFor?(instant: Temporal.Instant | string): string; @@ -1102,7 +1102,7 @@ export namespace Temporal { getNextTransition?(startingPoint: Temporal.Instant | string): Temporal.Instant | null; getPreviousTransition?(startingPoint: Temporal.Instant | string): Temporal.Instant | null; getPossibleInstantsFor(dateTime: Temporal.PlainDateTime | PlainDateTimeLike | string): Temporal.Instant[]; - toString(): string; + toString?(): string; toJSON?(): string; } diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 72e2d21a5f..22a0c1f74d 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -1999,8 +1999,8 @@ export const ES = ObjectAssign({}, ES2022, { }, TimeZoneEquals: (one, two) => { if (one === two) return true; - const tz1 = ES.ToString(one); - const tz2 = ES.ToString(two); + const tz1 = ES.ToTemporalTimeZoneIdentifier(one); + const tz2 = ES.ToTemporalTimeZoneIdentifier(two); return tz1 === tz2; }, TemporalDateTimeToDate: (dateTime) => { @@ -2450,8 +2450,9 @@ export const ES = ObjectAssign({}, ES2022, { result += ES.FormatISOTimeZoneOffsetString(offsetNs); } if (showTimeZone !== 'never') { + const identifier = ES.ToTemporalTimeZoneIdentifier(tz); const flag = showTimeZone === 'critical' ? '!' : ''; - result += `[${flag}${tz}]`; + result += `[${flag}${identifier}]`; } result += ES.MaybeFormatCalendarAnnotation(GetSlot(zdt, CALENDAR), showCalendar); return result; diff --git a/polyfill/lib/intl.mjs b/polyfill/lib/intl.mjs index 3feada85e7..a0ee6899a5 100644 --- a/polyfill/lib/intl.mjs +++ b/polyfill/lib/intl.mjs @@ -473,7 +473,7 @@ function extractOverrides(temporalObj, main) { } let timeZone = GetSlot(temporalObj, TIME_ZONE); - const objTimeZone = ES.ToString(timeZone); + const objTimeZone = ES.ToTemporalTimeZoneIdentifier(timeZone); if (main[TZ_GIVEN] && main[TZ_GIVEN] !== objTimeZone) { throw new RangeError(`timeZone option ${main[TZ_GIVEN]} doesn't match actual time zone ${objTimeZone}`); } diff --git a/spec/intl.html b/spec/intl.html index 76b2cbf83a..4e4952ef38 100644 --- a/spec/intl.html +++ b/spec/intl.html @@ -903,7 +903,7 @@

HandleDateTimeTemporalZonedDateTime ( _dateTimeFormat_, _zonedDateTime_ )TimeZoneEquals ( _one_, _two_ )

1. If _one_ and _two_ are the same Object value, return *true*. - 1. Let _timeZoneOne_ be ? ToString(_one_). - 1. Let _timeZoneTwo_ be ? ToString(_two_). + 1. Let _timeZoneOne_ be ? ToTemporalTimeZoneIdentifier(_one_). + 1. Let _timeZoneTwo_ be ? ToTemporalTimeZoneIdentifier(_two_). 1. If _timeZoneOne_ is _timeZoneTwo_, return *true*. 1. Return *false*. diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index b7732d4691..91c6554d53 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -1254,9 +1254,9 @@

1. If _showTimeZone_ is *"never"*, then 1. Let _timeZoneString_ be the empty String. 1. Else, - 1. Let _timeZoneID_ be ? ToString(_timeZone_). + 1. Let _timeZoneIdentifier_ be ? ToTemporalTimeZoneIdentifier(_timeZone_). 1. If _showTimeZone_ is *"critical"*, let _flag_ be *"!"*; else let _flag_ be the empty String. - 1. Let _timeZoneString_ be the string-concatenation of the code unit 0x005B (LEFT SQUARE BRACKET), _flag_, _timeZoneID_, and the code unit 0x005D (RIGHT SQUARE BRACKET). + 1. Let _timeZoneString_ be the string-concatenation of the code unit 0x005B (LEFT SQUARE BRACKET), _flag_, _timeZoneIdentifier_, and the code unit 0x005D (RIGHT SQUARE BRACKET). 1. Let _calendarString_ be ? MaybeFormatCalendarAnnotation(_zonedDateTime_.[[Calendar]], _showCalendar_). 1. Return the string-concatenation of _dateTimeString_, _offsetString_, _timeZoneString_, and _calendarString_.