Skip to content

Normative: In InitializeDateTimeFormat options, Temporal object or ISO string forms of calendar and timeZone will throw #2005

Closed
@justingrant

Description

@justingrant

While working on another fix, I noticed unexpected spec text and polyfill behavior for toLocaleString's calendar and timeZone options.

From js-temporal/temporal-polyfill#118:

the spec and the polyfill also accept a ZDT-like property bag too, per tc39/proposal-temporal#925. Specifically, they accept any object with a timeZone property and will use that property's value as the time zone, with one exception: per tc39/proposal-temporal#925 (comment), recursive bags like {timeZone: {timeZone: 'Asia/Tokyo'}} are disallowed.

The above is also true for calendars.

Update 2022-02-13: looks like the same spec text also breaks using ISO strings like "2020-04-25[Europe/Paris][u-ca=islamic]" for the calendar or timeZone options.

Update 2023-05-12: property bag forms of these options are no longer supported. So this issue is now scoped to just Temporal objects and ISO strings.

But AFAICT, a timezone-like or calendar-like object is currently accepted in all places where a TimeZone instance and timezone/calendar ID string are accepted... except toLocaleString methods (and the Temporal-aware Intl.DateTimeFormat constructor). The spec text for these operations coerce the timeZone or calendar option to a string, and then use that string. But if the option is an ISO string or a ZonedDateTime or Plain* instance, then InitializeDateTimeFormat will throw because coercing the option to a string yields something that will never be a timezone nor calendar ID.

I'm assuming that this is a spec bug, because AFAIK our intent was that all places where a calendar or time zone is required will either accept a Temporal.Calendar/Temporal.TimeZone instance, a string ID, an ISO string like 2020-01-01T00:00Z[America/Los_Angeles][u-ca=chinese] or an object like Temporal.ZonedDateTime.

Repro:

zdt = Temporal.ZonedDateTime.from('2000-01-01[Asia/Tokyo][u-ca=japanese]');
// => 2000-01-01T00:00:00+09:00[Asia/Tokyo][u-ca=japanese]
zdt2 = Temporal.ZonedDateTime.from({ year: 2000, month: 1, day: 1, calendar: zdt, timeZone: zdt });
// => Temporal.ZonedDateTime <2000-01-01T00:00:00+09:00[Asia/Tokyo][u-ca=japanese]
zdt.toLocaleString('ja-JP', { calendar: zdt, timeZone: zdt });
// => Uncaught RangeError: Invalid calendar : 2000-01-01T00:00:00+09:00[Asia/Tokyo][u-ca=japanese]

The problematic spec text is here:

### for calendars

6. Let calendar be ? GetOption(options, "calendar", "string", undefined, undefined).
7. If calendar is not undefined, then
  a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.

### for time zones

32. Else,
  a. Let timeZone be ? ToString(timeZone).
  b. If the result of IsValidTimeZoneName(timeZone) is false, then
    i. Throw a RangeError exception.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ecma402Behavior specific to implementations supporting ecma402has-consensusnon-prod-polyfillTHIS POLYFILL IS NOT FOR PRODUCTION USE!spec-textSpecification text involved

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions