How to get list of valid time zone identifiers
Retrieve all supported IANA time zone identifiers in JavaScript for building dropdowns and validating user input
Introduction
When building features that let users select their time zone, you need to know which time zone identifiers are valid. A user in New York might need to choose America/New_York, while a user in Tokyo needs Asia/Tokyo. If you hardcode a list of time zones, you create several problems.
First, the list becomes outdated. Time zone definitions change when governments modify daylight saving rules or create new time zones. Second, different JavaScript environments support different time zones. A time zone that works in Chrome might not work in an older browser or Node.js version. Third, maintaining hundreds of time zone strings by hand creates opportunities for typos and inconsistencies.
JavaScript provides the Intl.supportedValuesOf() method to retrieve all time zone identifiers that the current environment supports. This ensures your application only offers time zones that work correctly and stays current with changes to the time zone database.
What time zone identifiers are
Time zone identifiers are standardized strings that represent geographic regions with consistent time rules. They come from the IANA Time Zone Database, a comprehensive list maintained by an international organization that tracks time zones and daylight saving changes worldwide.
The identifiers use a specific format that makes them unambiguous and stable across different systems. Understanding this format helps you work with time zones effectively.
Understanding the format of IANA time zone identifiers
IANA time zone identifiers follow the pattern Area/Location, where the area represents a continent or ocean and the location represents a city or region within that area.
const examples = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney',
'Pacific/Auckland'
];
The area corresponds to:
Americafor North and South AmericaEuropefor European locationsAsiafor Asian locationsAfricafor African locationsAustraliafor Australian locationsPacificfor Pacific islandsAtlanticfor Atlantic islandsIndianfor Indian Ocean islandsAntarcticafor Antarctic research stations
The location typically represents the largest or most representative city in the time zone. New York represents the Eastern time zone of the United States. Tokyo represents Japan. Sydney represents the Eastern time zone of Australia.
Some identifiers include three parts for regions within a larger area:
const detailedExamples = [
'America/Indiana/Indianapolis',
'America/Kentucky/Louisville',
'America/North_Dakota/Center'
];
These multi-part identifiers distinguish regions that follow different time rules within the same country.
The database uses underscores instead of spaces in city names:
const underscoreExamples = [
'America/New_York', // Not "New York"
'America/Los_Angeles', // Not "Los Angeles"
'Asia/Ho_Chi_Minh' // Not "Ho Chi Minh"
];
This format ensures identifiers work as single tokens without special characters that could cause parsing issues.
Why identifiers use cities instead of abbreviations
You might expect to use abbreviations like EST for Eastern Standard Time or PST for Pacific Standard Time. However, these abbreviations are ambiguous. EST means Eastern Standard Time in North America but also means Australian Eastern Standard Time. CST could mean Central Standard Time, China Standard Time, or Cuba Standard Time.
City-based identifiers remain unambiguous. America/New_York always refers to the same place and time rules, regardless of context.
Additionally, abbreviations do not capture daylight saving transitions. EST only represents standard time, not EDT (Eastern Daylight Time). The identifier America/New_York automatically handles both standard and daylight time based on the date.
Getting all supported time zone identifiers
The Intl.supportedValuesOf() method with the 'timeZone' parameter returns an array of all time zone identifiers supported by the JavaScript environment.
const timeZones = Intl.supportedValuesOf('timeZone');
console.log(timeZones.length);
// Output: over 400
console.log(timeZones.slice(0, 10));
// Output: [
// "Africa/Abidjan",
// "Africa/Accra",
// "Africa/Addis_Ababa",
// "Africa/Algiers",
// "Africa/Asmera",
// "Africa/Bamako",
// "Africa/Bangui",
// "Africa/Banjul",
// "Africa/Bissau",
// "Africa/Blantyre"
// ]
The method returns the identifiers in alphabetical order with no duplicates. The exact list depends on the JavaScript environment, but modern browsers and Node.js versions support over 400 time zones.
Each identifier in the array can be used with Intl.DateTimeFormat to format dates and times for that time zone:
const timeZone = timeZones[0]; // "Africa/Abidjan"
const date = new Date('2025-10-15T12:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: timeZone,
dateStyle: 'long',
timeStyle: 'short'
});
console.log(formatter.format(date));
// Output: "October 15, 2025 at 12:00 PM"
The method guarantees that every identifier it returns works correctly with the Intl APIs.
Building a time zone selector
The most common use case for getting all time zone identifiers is building a dropdown or select element that lets users choose their time zone.
function buildTimeZoneSelector() {
const timeZones = Intl.supportedValuesOf('timeZone');
const select = document.createElement('select');
select.name = 'timeZone';
timeZones.forEach(timeZone => {
const option = document.createElement('option');
option.value = timeZone;
option.textContent = timeZone;
select.appendChild(option);
});
return select;
}
const selector = buildTimeZoneSelector();
document.body.appendChild(selector);
This creates a select element with options for every supported time zone. Users can scroll through the list and select their location.
However, a list of over 400 unsorted identifiers creates a poor user experience. Users need to understand how the identifiers are organized and find their location quickly.
Grouping time zones by region
Organizing time zones by continent makes the list easier to navigate. You can extract the region from each identifier and group them accordingly.
function groupTimeZonesByRegion() {
const timeZones = Intl.supportedValuesOf('timeZone');
const grouped = {};
timeZones.forEach(timeZone => {
const parts = timeZone.split('/');
const region = parts[0];
if (!grouped[region]) {
grouped[region] = [];
}
grouped[region].push(timeZone);
});
return grouped;
}
const grouped = groupTimeZonesByRegion();
console.log(Object.keys(grouped));
// Output: [
// "Africa", "America", "Antarctica", "Arctic",
// "Asia", "Atlantic", "Australia", "Europe",
// "Indian", "Pacific", "Etc"
// ]
console.log(grouped['America'].slice(0, 5));
// Output: [
// "America/Adak",
// "America/Anchorage",
// "America/Anguilla",
// "America/Antigua",
// "America/Araguaina"
// ]
The function splits each identifier on the forward slash and uses the first part as the region key. This creates an object where each region contains an array of time zones.
You can use this grouped data to build a more organized selector with optgroup elements:
function buildGroupedTimeZoneSelector() {
const grouped = groupTimeZonesByRegion();
const select = document.createElement('select');
select.name = 'timeZone';
Object.keys(grouped).sort().forEach(region => {
const optgroup = document.createElement('optgroup');
optgroup.label = region;
grouped[region].forEach(timeZone => {
const option = document.createElement('option');
option.value = timeZone;
option.textContent = timeZone.split('/').slice(1).join('/');
optgroup.appendChild(option);
});
select.appendChild(optgroup);
});
return select;
}
const groupedSelector = buildGroupedTimeZoneSelector();
document.body.appendChild(groupedSelector);
This creates a select element where time zones appear under their regional headings. The option text shows only the city portion of the identifier, making the list more readable.
Filtering time zones by region
Sometimes you only need time zones from specific regions. For example, an application serving only North American users might only show American time zones.
function getTimeZonesForRegion(region) {
const timeZones = Intl.supportedValuesOf('timeZone');
return timeZones.filter(timeZone => timeZone.startsWith(`${region}/`));
}
const americanTimeZones = getTimeZonesForRegion('America');
console.log(americanTimeZones.length);
// Output: over 150
console.log(americanTimeZones.slice(0, 5));
// Output: [
// "America/Adak",
// "America/Anchorage",
// "America/Anguilla",
// "America/Antigua",
// "America/Araguaina"
// ]
This function filters the complete list to include only identifiers that start with the specified region. You can call it with any region name to get a focused list.
You can filter for multiple regions:
function getTimeZonesForRegions(regions) {
const timeZones = Intl.supportedValuesOf('timeZone');
return timeZones.filter(timeZone => {
return regions.some(region => timeZone.startsWith(`${region}/`));
});
}
const europeanAndAsianTimeZones = getTimeZonesForRegions(['Europe', 'Asia']);
console.log(europeanAndAsianTimeZones.length);
// Output: over 200
This provides flexibility for applications that serve multiple but not all regions.
Searching for specific time zones
When users know the city they are looking for, searching by name helps them find it quickly.
function searchTimeZones(query) {
const timeZones = Intl.supportedValuesOf('timeZone');
const lowerQuery = query.toLowerCase();
return timeZones.filter(timeZone => {
return timeZone.toLowerCase().includes(lowerQuery);
});
}
const newYorkResults = searchTimeZones('new_york');
console.log(newYorkResults);
// Output: ["America/New_York"]
const londonResults = searchTimeZones('london');
console.log(londonResults);
// Output: ["Europe/London"]
const tokyoResults = searchTimeZones('tokyo');
console.log(tokyoResults);
// Output: ["Asia/Tokyo"]
This function performs a case-insensitive search across all time zone identifiers. Users can type part of a city name to find matching time zones.
For a better user experience, you can search for partial matches and handle spaces:
function searchTimeZonesFlexible(query) {
const timeZones = Intl.supportedValuesOf('timeZone');
const normalizedQuery = query.toLowerCase().replace(/\s+/g, '_');
return timeZones.filter(timeZone => {
return timeZone.toLowerCase().includes(normalizedQuery);
});
}
const results = searchTimeZonesFlexible('new york');
console.log(results);
// Output: ["America/New_York"]
This version converts spaces in the query to underscores, matching the format used in time zone identifiers.
Validating time zone identifiers
When users provide time zone identifiers as input, you need to verify that the identifier is valid before using it with Intl.DateTimeFormat.
function isValidTimeZone(timeZone) {
const supportedTimeZones = Intl.supportedValuesOf('timeZone');
return supportedTimeZones.includes(timeZone);
}
console.log(isValidTimeZone('America/New_York'));
// Output: true
console.log(isValidTimeZone('Europe/London'));
// Output: true
console.log(isValidTimeZone('Invalid/TimeZone'));
// Output: false
console.log(isValidTimeZone('EST'));
// Output: false
This function checks whether a given string appears in the list of supported time zones. If the identifier is invalid, you can reject it or prompt the user to choose from valid options.
You can also try to use the identifier and catch errors:
function validateTimeZoneByCatch(timeZone) {
try {
new Intl.DateTimeFormat('en-US', { timeZone });
return true;
} catch (error) {
return false;
}
}
console.log(validateTimeZoneByCatch('America/New_York'));
// Output: true
console.log(validateTimeZoneByCatch('Invalid/TimeZone'));
// Output: false
This approach works but is less efficient than checking the supported values list. Use the supported values check when performance matters.
Showing current offsets for time zones
Users often think about time zones in terms of their offset from UTC. Showing the current offset alongside each time zone name helps users understand the time difference.
function getTimeZoneOffset(timeZone) {
const date = new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone,
timeZoneName: 'shortOffset'
});
const parts = formatter.formatToParts(date);
const offsetPart = parts.find(part => part.type === 'timeZoneName');
return offsetPart ? offsetPart.value : '';
}
const timeZones = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney'
];
timeZones.forEach(timeZone => {
const offset = getTimeZoneOffset(timeZone);
console.log(`${timeZone}: ${offset}`);
});
// Output:
// America/New_York: GMT-4
// Europe/London: GMT+1
// Asia/Tokyo: GMT+9
// Australia/Sydney: GMT+11
The function formats a date with the shortOffset time zone name style, then extracts the offset portion from the formatted parts. This shows how many hours ahead or behind UTC each time zone is.
Note that offsets change with daylight saving time. The same time zone shows different offsets in winter and summer:
const newYorkWinter = getTimeZoneOffset('America/New_York');
// In January: GMT-5
const newYorkSummer = getTimeZoneOffset('America/New_York');
// In July: GMT-4
The offset returned by this function reflects the current date, so it updates automatically as daylight saving rules change throughout the year.
Building a complete time zone selector with offsets
Combining grouping, filtering, and offset display creates a comprehensive time zone selector:
function buildCompleteTimeZoneSelector() {
const timeZones = Intl.supportedValuesOf('timeZone');
const select = document.createElement('select');
select.name = 'timeZone';
const grouped = {};
timeZones.forEach(timeZone => {
const region = timeZone.split('/')[0];
if (!grouped[region]) {
grouped[region] = [];
}
grouped[region].push(timeZone);
});
Object.keys(grouped).sort().forEach(region => {
const optgroup = document.createElement('optgroup');
optgroup.label = region;
grouped[region].forEach(timeZone => {
const offset = getTimeZoneOffset(timeZone);
const location = timeZone.split('/').slice(1).join('/');
const option = document.createElement('option');
option.value = timeZone;
option.textContent = `${location} (${offset})`;
optgroup.appendChild(option);
});
select.appendChild(optgroup);
});
return select;
}
const completeSelector = buildCompleteTimeZoneSelector();
document.body.appendChild(completeSelector);
This selector groups time zones by region, shows readable location names, and includes current UTC offsets. Users can quickly find their time zone by region and verify the offset matches their expectations.
Getting the user's current time zone
While getting all supported time zones is useful for selection interfaces, you often want to know the user's current time zone to use as a default.
function getUserTimeZone() {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
const userTimeZone = getUserTimeZone();
console.log(userTimeZone);
// Output: "America/New_York" (or user's actual time zone)
This returns the IANA identifier for the user's system time zone. You can use this to pre-select the correct option in a time zone selector:
function buildTimeZoneSelectorWithDefault() {
const selector = buildCompleteTimeZoneSelector();
const userTimeZone = getUserTimeZone();
const options = selector.querySelectorAll('option');
options.forEach(option => {
if (option.value === userTimeZone) {
option.selected = true;
}
});
return selector;
}
const selectorWithDefault = buildTimeZoneSelectorWithDefault();
document.body.appendChild(selectorWithDefault);
This creates a selector with the user's current time zone already selected, reducing friction when users confirm their location.
Handling special time zones
The supported time zones list includes some special identifiers that do not follow the standard Area/Location pattern.
const timeZones = Intl.supportedValuesOf('timeZone');
const specialTimeZones = timeZones.filter(tz => !tz.includes('/'));
console.log(specialTimeZones);
// Output: ["UTC"]
The UTC identifier represents Coordinated Universal Time, which has no offset and no daylight saving changes. This identifier is useful when you want to display times in a universal reference frame rather than a local time zone.
Some environments include additional special identifiers like GMT or zone offsets like Etc/GMT+5. You can filter these out if your application only needs standard geographic time zones:
function getGeographicTimeZones() {
const timeZones = Intl.supportedValuesOf('timeZone');
return timeZones.filter(timeZone => {
return timeZone.includes('/') && !timeZone.startsWith('Etc/');
});
}
const geographicTimeZones = getGeographicTimeZones();
console.log(geographicTimeZones.length);
// Output: over 400
This filters out non-geographic identifiers, leaving only standard city-based time zones.
Understanding time zone aliases
The IANA database includes multiple identifiers that refer to the same time zone rules. For example, Asia/Calcutta and Asia/Kolkata both refer to Indian Standard Time, but Kolkata is the modern name.
The Intl.supportedValuesOf() method returns the canonical identifiers. If users provide an alias, it still works with Intl.DateTimeFormat:
const canonicalFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Kolkata',
timeZoneName: 'long'
});
const aliasFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Calcutta',
timeZoneName: 'long'
});
const date = new Date('2025-10-15T12:00:00Z');
console.log(canonicalFormatter.format(date));
// Output: time in Indian Standard Time
console.log(aliasFormatter.format(date));
// Output: time in Indian Standard Time
Both formatters produce the same result because the alias maps to the canonical identifier internally.
However, the supported values list only includes canonical identifiers. If you are validating user input, consider normalizing aliases to their canonical forms:
function normalizeTimeZone(timeZone) {
try {
const formatter = new Intl.DateTimeFormat('en-US', { timeZone });
return formatter.resolvedOptions().timeZone;
} catch (error) {
return null;
}
}
console.log(normalizeTimeZone('Asia/Calcutta'));
// Output: "Asia/Kolkata"
console.log(normalizeTimeZone('America/New_York'));
// Output: "America/New_York"
console.log(normalizeTimeZone('Invalid/Zone'));
// Output: null
This function creates a formatter with the provided time zone and extracts the canonical identifier from the resolved options. If the identifier is invalid, the formatter throws an error and the function returns null.
Browser support and compatibility
The Intl.supportedValuesOf() method is available in modern browsers and Node.js versions:
- Chrome 99 and later
- Firefox 93 and later
- Safari 15.4 and later
- Edge 99 and later
- Node.js 18.0.0 and later
For older environments, you can feature-detect the method and provide a fallback:
function getSupportedTimeZones() {
if (typeof Intl.supportedValuesOf === 'function') {
return Intl.supportedValuesOf('timeZone');
}
return [
'Africa/Cairo',
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Los_Angeles',
'Asia/Dubai',
'Asia/Kolkata',
'Asia/Tokyo',
'Australia/Sydney',
'Europe/London',
'Europe/Paris',
'Pacific/Auckland',
'UTC'
];
}
const timeZones = getSupportedTimeZones();
This function checks whether Intl.supportedValuesOf exists and returns the full list if available. If not, it returns a smaller hardcoded list of common time zones. The fallback ensures your application works in older environments while providing the full list in modern ones.