如何获取有效时区标识符列表
在 JavaScript 中检索所有支持的 IANA 时区标识符,用于构建下拉菜单和验证用户输入
介绍
在构建允许用户选择其时区的功能时,您需要知道哪些时区标识符是有效的。位于纽约的用户可能需要选择 America/New_York,而位于东京的用户则需要选择 Asia/Tokyo。如果您硬编码一个时区列表,会导致几个问题。
首先,该列表会变得过时。当政府修改夏令时规则或创建新时区时,时区定义会发生变化。其次,不同的 JavaScript 环境支持不同的时区。在 Chrome 中有效的时区可能在较旧的浏览器或 Node.js 版本中无效。第三,手动维护数百个时区字符串会增加出现拼写错误和不一致的机会。
JavaScript 提供了 Intl.supportedValuesOf() 方法来检索当前环境支持的所有时区标识符。这确保了您的应用程序只提供正确工作的时区,并能随时区数据库的变化保持更新。
什么是时区标识符
时区标识符是表示具有一致时间规则的地理区域的标准化字符串。它们来自 IANA 时区数据库,这是一个由国际组织维护的全面列表,跟踪全球时区和夏令时的变化。
这些标识符使用特定的格式,使它们在不同系统中具有明确性和稳定性。理解这种格式有助于您有效地处理时区。
理解 IANA 时区标识符的格式
IANA 时区标识符遵循 Area/Location 的模式,其中 Area 表示大陆或海洋,Location 表示该区域内的城市或地区。
const examples = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney',
'Pacific/Auckland'
];
Area 对应于:
America表示北美和南美Europe表示欧洲地区Asia表示亚洲地区Africa表示非洲地区Australia表示澳大利亚地区Pacific表示太平洋岛屿Atlantic表示大西洋岛屿Indian表示印度洋岛屿Antarctica表示南极洲研究站
Location 通常表示时区内最大或最具代表性的城市。纽约代表美国东部时区,东京代表日本,悉尼代表澳大利亚东部时区。
一些标识符包括三个部分,用于区分同一国家内遵循不同时间规则的地区:
const detailedExamples = [
'America/Indiana/Indianapolis',
'America/Kentucky/Louisville',
'America/North_Dakota/Center'
];
这些多部分标识符区分了同一国家内遵循不同时间规则的地区。
数据库在城市名称中使用下划线代替空格:
const underscoreExamples = [
'America/New_York', // 不是 "New York"
'America/Los_Angeles', // 不是 "Los Angeles"
'Asia/Ho_Chi_Minh' // 不是 "Ho Chi Minh"
];
这种格式确保标识符可以作为单个标记使用,而不会因特殊字符导致解析问题。
为什么标识符使用城市名称而不是缩写
您可能会期望使用像 EST(东部标准时间)或 PST(太平洋标准时间)这样的缩写。然而,这些缩写是模棱两可的。EST 在北美表示东部标准时间,但也可以表示澳大利亚东部标准时间。CST 可能表示中央标准时间、中国标准时间或古巴标准时间。
基于城市的标识符始终是明确的。America/New_York 始终指代相同的地点和时间规则,无论上下文如何。
此外,缩写无法涵盖夏令时的转换。EST 仅表示标准时间,而不包括 EDT(东部夏令时间)。标识符 America/New_York 会根据日期自动处理标准时间和夏令时间。
获取所有支持的时区标识符
Intl.supportedValuesOf() 方法与 'timeZone' 参数一起使用时,会返回一个 JavaScript 环境支持的所有时区标识符的数组。
const timeZones = Intl.supportedValuesOf('timeZone');
console.log(timeZones.length);
// 输出:超过 400
console.log(timeZones.slice(0, 10));
// 输出:[
// "Africa/Abidjan",
// "Africa/Accra",
// "Africa/Addis_Ababa",
// "Africa/Algiers",
// "Africa/Asmera",
// "Africa/Bamako",
// "Africa/Bangui",
// "Africa/Banjul",
// "Africa/Bissau",
// "Africa/Blantyre"
// ]
该方法返回按字母顺序排列且无重复的标识符列表。具体列表取决于 JavaScript 环境,但现代浏览器和 Node.js 版本支持超过 400 个时区。
数组中的每个标识符都可以与 Intl.DateTimeFormat 一起使用,以格式化该时区的日期和时间:
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));
// 输出:"2025年10月15日 下午12:00"
该方法保证它返回的每个标识符都可以与 Intl API 正确配合使用。
构建时区选择器
获取所有时区标识符的最常见用例是构建一个下拉菜单或选择元素,让用户选择他们的时区。
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);
这将创建一个包含所有支持的时区选项的选择元素。用户可以滚动列表并选择他们的位置。
然而,超过 400 个未排序的标识符列表会带来糟糕的用户体验。用户需要了解标识符的组织方式并快速找到他们的位置。
按区域分组时区
按大陆组织时区可以使列表更易于导航。您可以从每个标识符中提取区域并相应地对其进行分组。
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));
// 输出: [
// "Africa", "America", "Antarctica", "Arctic",
// "Asia", "Atlantic", "Australia", "Europe",
// "Indian", "Pacific", "Etc"
// ]
console.log(grouped['America'].slice(0, 5));
// 输出: [
// "America/Adak",
// "America/Anchorage",
// "America/Anguilla",
// "America/Antigua",
// "America/Araguaina"
// ]
该函数通过正斜杠分割每个标识符,并使用第一部分作为区域键。这会创建一个对象,其中每个区域包含一个时区数组。
您可以使用这些分组数据构建一个更有组织的选择器,其中包含 optgroup 元素:
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);
这将创建一个选择元素,其中时区出现在其区域标题下。选项文本仅显示标识符的城市部分,使列表更易于阅读。
按区域筛选时区
有时您只需要特定区域的时区。例如,仅为北美用户服务的应用程序可能只显示美洲的时区。
function getTimeZonesForRegion(region) {
const timeZones = Intl.supportedValuesOf('timeZone');
return timeZones.filter(timeZone => timeZone.startsWith(`${region}/`));
}
const americanTimeZones = getTimeZonesForRegion('America');
console.log(americanTimeZones.length);
// 输出:超过 150
console.log(americanTimeZones.slice(0, 5));
// 输出:[
// "America/Adak",
// "America/Anchorage",
// "America/Anguilla",
// "America/Antigua",
// "America/Araguaina"
// ]
此函数会筛选完整列表,仅包含以指定区域开头的标识符。您可以使用任何区域名称调用它以获取特定的列表。
您还可以筛选多个区域:
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);
// 输出:超过 200
这为服务多个但不是所有区域的应用程序提供了灵活性。
搜索特定时区
当用户知道他们正在寻找的城市时,通过名称搜索可以帮助他们快速找到。
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);
// 输出:["America/New_York"]
const londonResults = searchTimeZones('london');
console.log(londonResults);
// 输出:["Europe/London"]
const tokyoResults = searchTimeZones('tokyo');
console.log(tokyoResults);
// 输出:["Asia/Tokyo"]
此函数在所有时区标识符中执行不区分大小写的搜索。用户可以输入部分城市名称以找到匹配的时区。
为了提供更好的用户体验,您可以搜索部分匹配并处理空格:
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);
// 输出:["America/New_York"]
此版本将查询中的空格转换为下划线,以匹配时区标识符中使用的格式。
验证时区标识符
当用户提供时区标识符作为输入时,需要在将其与 Intl.DateTimeFormat 一起使用之前验证该标识符是否有效。
function isValidTimeZone(timeZone) {
const supportedTimeZones = Intl.supportedValuesOf('timeZone');
return supportedTimeZones.includes(timeZone);
}
console.log(isValidTimeZone('America/New_York'));
// 输出: true
console.log(isValidTimeZone('Europe/London'));
// 输出: true
console.log(isValidTimeZone('Invalid/TimeZone'));
// 输出: false
console.log(isValidTimeZone('EST'));
// 输出: false
此函数检查给定字符串是否出现在支持的时区列表中。如果标识符无效,可以拒绝它或提示用户从有效选项中选择。
您还可以尝试使用标识符并捕获错误:
function validateTimeZoneByCatch(timeZone) {
try {
new Intl.DateTimeFormat('en-US', { timeZone });
return true;
} catch (error) {
return false;
}
}
console.log(validateTimeZoneByCatch('America/New_York'));
// 输出: true
console.log(validateTimeZoneByCatch('Invalid/TimeZone'));
// 输出: false
这种方法有效,但效率低于检查支持的值列表。当性能很重要时,请使用支持的值检查。
显示时区的当前偏移量
用户通常通过与 UTC 的偏移量来理解时区。显示每个时区名称旁边的当前偏移量有助于用户了解时间差。
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}`);
});
// 输出:
// America/New_York: GMT-4
// Europe/London: GMT+1
// Asia/Tokyo: GMT+9
// Australia/Sydney: GMT+11
该函数使用 shortOffset 时区名称样式格式化日期,然后从格式化的部分中提取偏移量部分。这显示了每个时区相对于 UTC 的小时数。
请注意,偏移量会随着夏令时的变化而变化。同一时区在冬季和夏季显示不同的偏移量:
const newYorkWinter = getTimeZoneOffset('America/New_York');
// 一月: GMT-5
const newYorkSummer = getTimeZoneOffset('America/New_York');
// 七月: GMT-4
此函数返回的偏移量反映当前日期,因此会随着一年中夏令时规则的变化自动更新。
构建一个带有偏移量的完整时区选择器
结合分组、过滤和偏移量显示,可以创建一个全面的时区选择器:
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);
此选择器按区域分组时区,显示可读的地点名称,并包含当前的 UTC 偏移量。用户可以通过区域快速找到他们的时区,并验证偏移量是否符合预期。
获取用户的当前时区
虽然获取所有支持的时区对于选择界面很有用,但您通常需要知道用户的当前时区以用作默认值。
function getUserTimeZone() {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
const userTimeZone = getUserTimeZone();
console.log(userTimeZone);
// 输出: "America/New_York"(或用户的实际时区)
这将返回用户系统时区的 IANA 标识符。您可以使用它在时区选择器中预先选择正确的选项:
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);
这将创建一个选择器,其中用户的当前时区已被选中,从而减少用户确认其位置时的阻力。
处理特殊时区
支持的时区列表中包含一些不遵循标准 Area/Location 格式的特殊标识符。
const timeZones = Intl.supportedValuesOf('timeZone');
const specialTimeZones = timeZones.filter(tz => !tz.includes('/'));
console.log(specialTimeZones);
// 输出: ["UTC"]
UTC 标识符表示协调世界时(Coordinated Universal Time),它没有偏移量,也没有夏令时变化。当您希望以通用参考框架而非本地时区显示时间时,此标识符非常有用。
某些环境中还包括额外的特殊标识符,例如 GMT 或类似 Etc/GMT+5 的区域偏移。您可以将这些过滤掉,如果您的应用程序只需要标准的地理时区:
function getGeographicTimeZones() {
const timeZones = Intl.supportedValuesOf('timeZone');
return timeZones.filter(timeZone => {
return timeZone.includes('/') && !timeZone.startsWith('Etc/');
});
}
const geographicTimeZones = getGeographicTimeZones();
console.log(geographicTimeZones.length);
// 输出: 超过 400
这会过滤掉非地理标识符,仅保留基于城市的标准时区。
理解时区别名
IANA 数据库中包含多个指向相同时区规则的标识符。例如,Asia/Calcutta 和 Asia/Kolkata 都指向印度标准时间(Indian Standard Time),但 Kolkata 是现代名称。
Intl.supportedValuesOf() 方法返回规范标识符。如果用户提供了别名,它仍然可以与 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));
// 输出: 印度标准时间的时间
console.log(aliasFormatter.format(date));
// 输出: 印度标准时间的时间
两个格式化器生成相同的结果,因为别名在内部映射到规范标识符。
然而,支持的值列表仅包含规范标识符。如果您正在验证用户输入,请考虑将别名规范化为其规范形式:
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'));
// 输出: "Asia/Kolkata"
console.log(normalizeTimeZone('America/New_York'));
// 输出: "America/New_York"
console.log(normalizeTimeZone('Invalid/Zone'));
// 输出: null
此函数使用提供的时区创建一个格式化器,并从解析选项中提取规范标识符。如果标识符无效,格式化器会抛出错误,函数返回 null。
浏览器支持和兼容性
Intl.supportedValuesOf() 方法可在现代浏览器和 Node.js 版本中使用:
- Chrome 99 及更高版本
- Firefox 93 及更高版本
- Safari 15.4 及更高版本
- Edge 99 及更高版本
- Node.js 18.0.0 及更高版本
对于较旧的环境,您可以检测该方法是否存在并提供回退方案:
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();
此函数会检查 Intl.supportedValuesOf 是否存在,如果存在则返回完整列表。如果不存在,则返回一个较小的常见时区的硬编码列表。该回退方案确保您的应用程序在较旧的环境中也能正常工作,同时在现代环境中提供完整列表。