Как получить список допустимых идентификаторов часовых поясов

Получите все поддерживаемые 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',    // Не "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('ru-RU', {
  timeZone: timeZone,
  dateStyle: 'long',
  timeStyle: 'short'
});

console.log(formatter.format(date));
// Вывод: "15 октября 2025 г. в 12:00"

Метод гарантирует, что каждый возвращаемый идентификатор корректно работает с API Intl.

Создание селектора часовых поясов

Наиболее распространенный случай использования для получения всех идентификаторов часовых поясов — это создание выпадающего списка или элемента выбора, который позволяет пользователям выбрать свой часовой пояс.

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 представляет всемирное координированное время, которое не имеет смещения и изменений на летнее время. Этот идентификатор полезен, когда вы хотите отображать время в универсальной системе отсчета, а не в локальном часовом поясе.

Некоторые среды включают дополнительные специальные идентификаторы, такие как 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 оба относятся к индийскому стандартному времени, но 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, и возвращает полный список, если он доступен. Если нет, возвращается небольшой предопределённый список общих часовых поясов. Этот подход обеспечивает работу вашего приложения в старых средах, предоставляя полный список в современных.