Как получить список допустимых идентификаторов часовых поясов
Получите все поддерживаемые IANA-идентификаторы часовых поясов в JavaScript для создания выпадающих списков и проверки пользовательского ввода
Введение
Когда вы создаёте функции, позволяющие пользователям выбирать свой часовой пояс, важно знать, какие идентификаторы часовых поясов считаются допустимыми. Пользователь из Нью-Йорка может выбрать America/New_York, а пользователь из Токио — Asia/Tokyo. Если вы жёстко задаёте список часовых поясов, это приводит к ряду проблем.
Во-первых, такой список быстро устаревает. Определения часовых поясов меняются, когда правительства корректируют правила перехода на летнее время или создают новые пояса. Во-вторых, разные среды JavaScript поддерживают разные часовые пояса. То, что работает в Chrome, может не работать в старом браузере или версии Node.js. В-третьих, ручное поддержание сотен строк с часовыми поясами приводит к опечаткам и несогласованности.
В JavaScript есть метод Intl.supportedValuesOf(), который позволяет получить все идентификаторы часовых поясов, поддерживаемые текущей средой. Это гарантирует, что ваше приложение предлагает только те пояса, которые действительно работают, и всегда актуально с учётом изменений в базе данных часовых поясов.
Что такое идентификаторы часовых поясов
Идентификаторы часовых поясов — это стандартизированные строки, которые обозначают географические регионы с едиными правилами времени. Они берутся из базы данных часовых поясов IANA — это полный список, который поддерживается международной организацией, отслеживающей изменения поясов и переходов на летнее время по всему миру.
Идентификаторы используют специальный формат, который делает их однозначными и стабильными на разных системах. Понимание этого формата помогает эффективно работать с часовыми поясами.
Формат IANA-идентификаторов часовых поясов
Идентификаторы часовых поясов IANA соответствуют шаблону Area/Location, где область обозначает континент или океан, а местоположение — город или регион внутри этой области.
const examples = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney',
'Pacific/Auckland'
];
Область соответствует:
Americaдля Северной и Южной АмерикиEuropeдля европейских локацийAsiaдля азиатских локацийAfricaдля африканских локацийAustraliaдля австралийских локацийPacificдля островов Тихого океанаAtlanticдля островов Атлантического океанаIndianдля островов Индийского океанаAntarcticaдля антарктических исследовательских станций
Местоположение обычно обозначает самый крупный или наиболее известный город в часовом поясе. Нью-Йорк представляет восточный часовой пояс США. Токио — Японию. Сидней — восточный часовой пояс Австралии.
Некоторые идентификаторы включают три части для регионов внутри одной большой области:
const detailedExamples = [
'America/Indiana/Indianapolis',
'America/Kentucky/Louisville',
'America/North_Dakota/Center'
];
Такие составные идентификаторы различают регионы, в которых действуют разные правила времени внутри одной страны.
В базе данных вместо пробелов в названиях городов используются подчёркивания:
const underscoreExamples = [
'America/New_York', // Not "New York"
'America/Los_Angeles', // Not "Los Angeles"
'Asia/Ho_Chi_Minh' // Not "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);
// 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"
// ]
Метод возвращает идентификаторы в алфавитном порядке без дубликатов. Точный список зависит от среды 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));
// Output: "October 15, 2025 at 12:00 PM"
Метод гарантирует, что каждый возвращённый идентификатор корректно работает с 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);
Это создаёт элемент select с опциями для каждого поддерживаемого часового пояса. Пользователь может пролистать список и выбрать своё местоположение.
Однако список из более чем 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));
// 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"
// ]
Функция разбивает каждый идентификатор по символу слэша и использует первую часть как ключ региона. В результате получается объект, где каждый регион содержит массив часовых поясов.
Эти сгруппированные данные можно использовать для создания более удобного селектора с элементами 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);
В результате создаётся элемент select, в котором часовые пояса отображаются под заголовками регионов. В тексте опций показывается только часть с названием города, что делает список более удобочитаемым.
Фильтрация часовых поясов по региону
Иногда нужны только часовые пояса определённых регионов. Например, приложение, рассчитанное только на пользователей из Северной Америки, может показывать только американские часовые пояса.
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"
// ]
Эта функция фильтрует полный список, оставляя только идентификаторы, начинающиеся с указанного региона. Её можно вызвать с любым названием региона, чтобы получить нужный список.
Можно фильтровать сразу по нескольким регионам:
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
Это даёт гибкость для приложений, которые обслуживают несколько, но не все регионы.
Поиск конкретных часовых поясов
Если пользователь знает нужный город, поиск по названию поможет быстро его найти.
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"]
Эта функция выполняет поиск без учёта регистра по всем идентификаторам часовых поясов. Пользователь может ввести часть названия города, чтобы найти подходящие варианты.
Для удобства можно искать по частичному совпадению и учитывать пробелы:
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"]
В этой версии пробелы в запросе заменяются на подчёркивания, чтобы соответствовать формату идентификаторов часовых поясов.
Проверка идентификаторов часовых поясов
Когда пользователи вводят идентификаторы часовых поясов, нужно проверить, что идентификатор действителен, прежде чем использовать его с 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
Эта функция проверяет, содержится ли заданная строка в списке поддерживаемых часовых поясов. Если идентификатор недействителен, вы можете отклонить его или предложить пользователю выбрать из допустимых вариантов.
Можно также попробовать использовать идентификатор и обработать ошибки:
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
Этот способ работает, но менее эффективен, чем проверка по списку поддерживаемых значений. Используйте проверку по списку, если важна производительность.
Отображение текущих смещений для часовых поясов
Пользователи часто воспринимают часовые пояса через их смещение относительно 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}`);
});
// Output:
// 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');
// In January: GMT-5
const newYorkSummer = getTimeZoneOffset('America/New_York');
// In July: 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);
// Output: "America/New_York" (or user's actual time zone)
Это возвращает идентификатор 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);
// Output: ["UTC"]
Идентификатор UTC обозначает всемирное координированное время (UTC), у которого нет смещения и перехода на летнее время. Этот идентификатор полезен, если нужно показывать время в универсальном формате, а не в локальном часовом поясе.
В некоторых средах есть дополнительные специальные идентификаторы, такие как 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);
// Output: over 400
Это фильтрует негеографические идентификаторы, оставляя только стандартные часовые пояса, основанные на городах.
Понимание алиасов часовых поясов
В базе данных IANA есть несколько идентификаторов, которые ссылаются на одни и те же правила часового пояса. Например, Asia/Calcutta и Asia/Kolkata оба относятся к индийскому стандартному времени, но 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));
// Output: time in Indian Standard Time
console.log(aliasFormatter.format(date));
// Output: time in Indian Standard Time
Оба форматтера выдают одинаковый результат, потому что псевдоним внутренне сопоставляется с каноническим идентификатором.
Однако в списке поддерживаемых значений указаны только канонические идентификаторы. Если вы проверяете пользовательский ввод, рекомендуется нормализовать псевдонимы до их канонических форм:
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
Эта функция создаёт форматтер с указанным часовым поясом и извлекает канонический идентификатор из полученных опций. Если идентификатор некорректен, форматтер выбрасывает ошибку, и функция возвращает 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, и возвращает полный список, если доступен. Если нет — возвращает небольшой жёстко заданный список популярных часовых поясов. Такой запасной вариант позволяет вашему приложению работать в старых средах, предоставляя полный список в современных.