Getting all canonical IANA ids
While I fully intend to support all IANA time zone ids in my application, when users select a timezone, I want the options to only contain currently canonical zones.
How would I get a list containing only canonical IANA time zone id’s in javascript?
Preferably, I would like a way to achieve this using javascript apis (Intl/Temporal – even if they are not widely available yet), but I’m also interested in how this might be achieved with luxon, dayjs, or other packages.
I am aware of Intl.supportedValuesOf('timeZone'). However, this returns a list of primary time zone identifiers, which
uniquely identify the time zone [and] usually refer to a geographic area anchored by a city…
and as such are a concept distinct from canonical ids, as described in this note:
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.
I am also aware of the tzdb package. However, this returns too narrow of a list. It’s rules for grouping are
- if the time zones are in the same country
- if the [DST and non-DST] offsets are the same
which, for example, means that America/Indiana/Indianapolis is grouped under America/New_York, despite being a distinct canonical id.
Getting canonical id for a given valid IANA id
How would I resolve an IANA time zone id to its canonical version (follow link if it is an alias, or return itself if it is already canonical)?
If I could figure this out, I could map the result of Intl.supportedValuesOf('timeZone') and take the distinct values.
This is also necessary to check time zone ids for semantic equality – America/Indianapolis should be considered equivalent to America/Indiana/Indianapolis, but since string equality obviously will fail, I need a way to resolve links.
My research thus far found this approach
function getCanonicalTimezone(ianaId) {
try {
const formatter = new Intl.DateTimeFormat('en', {
timeZone: ianaId
});
return formatter.resolvedOptions().timeZone;
} catch (error) {
throw new Error(`Invalid timezone: ${ianaId}`);
}
}
However, this doesn’t resolve aliases in every case. For example, Africa/Accra resolves to itself, even though it is an alias of Africa/Abidjan.
Bonus question / rant
Why does Temporal invent this new concept of primary time zone identifiers?
I understand that sometimes an alias is scoped to a country different than the id to which it links (as in the example I quoted), but then if they were intended to be treated differently, why would IANA have created the link relationship? Temporal is very subtly diverging from IANA in their terminology (and therefore behavior), and is setting up all but those who carefully read the documentation to be confused (and I can tell you from my experience over the last few weeks that llms are not up to speed on the distinction). Creating a new standard also has x-framework implications – e.g. in my case our api is not javascript so its lists will not match the front end if the front end is not following IANA standards.

