Skip to content

Intl.supportedValuesOf('timeZone') does not align with tzdb canonical time zones #3249

@acalvino4

Description

@acalvino4

Problem

After extensive research, I've come to the conclusion that there is no direct way to get a list of "canonical" timezone ids in javascript, either by using Date, Temporal, or any third-party library. I'm bringing up this issue here because it feels like something Temporal should solve. Why? Well, it seems like it tried to, but unfortunately isn't doing it in quite the right way in its current state. I'm of course talking about Intl.supportedValuesOf('timeZone').

At first glance this would seem to provide what I need, a list of supported time zone values. Well, what it actually returns per the docs:

Temporal requires browsers to always return the primary identifier in the IANA database ... the returned array should contain "Asia/Kolkata" instead of "Asia/Calcutta" because the latter is an alias of the former and they both correspond to India; however, it should contain both "Africa/Abidjan" and "Atlantic/Reykjavik" because they are in different countries, despite the latter also being an alias of the former.

(As an aside, I see that this clearly is a method on Intl, but I'm assuming based on the quote above that its behavior in this case is actually defined by Temporal; correct me if I'm mistaken)

So supportedValuesOf returns all time zones, except for aliases, though aliases are included when they reference another country (it also omits Etc zones, and sometimes returns the alias instead of the canonical id, but I'll come back to those issues later). This lines up with the description of a primary timezone found elsewhere:

A primary time zone identifier ... uniquely identifies the time zone. It usually refers to a geographic area anchored by a city ... Note that the attribution of primary identifiers preserves the country code: for example, the IANA database records Atlantic/Reykjavik as an alias for Africa/Abidjan, but because they correspond to different countries (Iceland and Côte d'Ivoire, respectively), they are treated as distinct primary identifiers.

How does that differ from what I'm looking for, that is, canonical ids? What is a canonical id? From wikipedia's list of tz db timezones (I don't see wikipedia as an authoritative source btw, but it is a much more convenient place to reference the list than digging through the individual tzdb files - which i have done btw - and furthermore it is directly linked by the mdn Temporal docs):

The primary, preferred zone name.

From looking through the actual list, you can see that the canonical ids are essentially all tz ids that are not links.

So, "canonical ids" as defined on wikipedia and "primary ids" as defined in the Temporal docs are different things. Canonical ids do not include "Atlantic/Reykjavik" or "Atlantic/St_Helena" because they are links, while primary ids do include them because the id itself and the id it links to represent different countries. Conversely, canonical ids do include the Etc zones, while Temporal's list does not.

Now, I am open to being convinced that wikipedia's definition is not accurate, or if accurate, not the classification we should be concerned about. I indeed couldn't find an authoritative source with their direct classification. However, the only tzdb file in which these link identifiers show up is the backward tzdb file, which says

This file provides links from old or merged timezone names to current ones.

Based off that note, wikipedia sure seems right; and it seems odd for Temporal/Intl to be considering Atlantic/Reykjavik and several others as primary tz ids when they are only listed in this file.

Now maybe there's some history or context that makes sense of Temporal's decisions in this regard, if so I'd love to understand better. I'm sure there are use cases for which the list provided by supportedValuesOf is exactly what is needed. However, it doesn't seem to me like it should be the default. It seems that by introducing this distinct concept of "primary tz identifiers" (at least, I haven't found a prior reference for this term), Temporal is subtly differing from IANA. For an initiative that was meant to bring (and in so many ways has brought!) sanity to the world of dates and times, this is concerning.

Desired solution

The use case that send me down this path is simple - provide a user with a dropdown of allowed time zones for them to make a selection. However, trying to think more generally, I think these are the things I would hope Temporal to provide in some form.

  • Way to get list of all supported tz identifiers (i.e. anything that is valid, including "GB", "HST", "Libya", "Etc/GMT-13", etc.)
  • Way to get list of only canonical identifier
  • Way to resolve link identifiers to canonical ids

I don't know if it sounds like I'm asking for a lot; it seems (correct me if mistaken) that this info is all there somewhere already behind the scenes and just not part of the public api (Temporal recognizes all valid ids based on my testing, and will sometimes resolve aliases using Intl.DateTimeFormat and resolvedOptions).

Essentially all you need is a object mapping tz identifiers to what they link to - looking at the wikipedia list again, this would be a dictionary where the keys come from the "TZ identifier" column, and the values come from the "Notes" column (entries without a link map to themselves). Say this object is called timeZoneMap - from there deriving all sorts of helper lists and utilities is trivial:

const getCanonicalTimeZoneId = (timeZoneId: string): string => timeZoneMap[timeZoneId]

const isTimeZoneIdValid = (timeZoneId: string): boolean => timeZoneId in timeZoneMap

const areTimeZonesEquivalent = (tzId1: string, tzId2: string): boolean => getCanonicalTimeZoneId(tzId1) === getCanonicalTimeZoneId(tzId2)

const canonicalTimeZoneIds = [...new Set(Object.values(timeZoneMap))]

As prior art, NodaTime in the C# world provides exactly this, under NodaTime.TimeZones.TzdbDateTimeZoneSource.Default.CanonicalIdMap

Additionally, I think associating Countries with timezone identifiers is cool (as is done in the wikipedia list), and probably useful to a lot of people. If you had a similar map to what i described above, people who need what supportedValuesOf currently provides could easily derive it from such a map, and supportedValuesOf could instead sensibly return a list of all supported values.

Summary

  • Temporal invented a concept of "primary time zone identifiers", which differs from IANA's canonical identifiers
  • This is probably undesired, bc it deviates from standards in including "old/merged" ids
  • Would be better to have supportedValuesOf('timeZone') return all supported values of timezone
  • Additionally would be extremely helpful to externally provide some of the mappings that are almost certainly being used internally, namely mapping from tz id's to the id's they link to, and mappings from id's to country they are associated with.

Side note:
supportedValuesOf doesn't even always follow its own rules. According to IANA, "Indiana/Indianapolis" is a link to ""America/Indiana/Indianapolis"; but supported values of (and DateTimeFormat.prototype.resolvedOptions), return "Indiana/Indianapolis", the link, instead of the id.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions