Получить список доступных календарных систем

Узнайте, какие календарные системы поддерживает ваша среда JavaScript

Введение

Когда вы создаёте приложения для глобальной аудитории, пользователям часто нужно видеть даты в предпочитаемой ими календарной системе. Хотя вам может быть знаком григорианский календарь, используемый в большинстве западных стран, во многих культурах применяются совершенно другие системы, такие как исламский календарь Хиджры, еврейский или буддийский календарь.

Чтобы дать пользователям возможность выбрать нужную календарную систему, важно знать, какие календари поддерживаются в среде JavaScript. Вместо того чтобы поддерживать жёстко заданный список, который быстро устаревает или содержит неподдерживаемые значения, JavaScript позволяет узнать доступные календарные системы во время выполнения.

Метод Intl.supportedValuesOf() с параметром "calendar" возвращает массив всех идентификаторов календарных систем, поддерживаемых в текущей среде. Это гарантирует, что ваше приложение предложит только те календари, которые действительно работают.

Что такое календарные системы

Календарные системы — это разные способы организации и отсчёта времени. Все календари отслеживают дни, месяцы и годы, но используют разные правила для определения начала года, продолжительности месяцев и учёта астрономических циклов.

Григорианский календарь, введённый в 1582 году, — самый распространённый гражданский календарь. Он основан на солнечном году продолжительностью примерно 365,25 дня, делится на 12 месяцев, а високосные годы добавляются каждые четыре года (за исключением некоторых столетий).

В других календарных системах действуют свои правила. Например, исламский календарь полностью лунный, месяцы в нём следуют фазам Луны. Поэтому исламский год примерно на 11 дней короче солнечного, и даты исламского календаря со временем смещаются по сезонам. Еврейский и китайский календари — лунно-солнечные, то есть сочетают лунные месяцы с корректировками для синхронизации с солнечным годом.

Когда вы форматируете даты в JavaScript, календарная система определяет, какие значения года, месяца и дня будут отображаться. Один и тот же момент времени может иметь разные представления даты в разных календарных системах.

Использование Intl.supportedValuesOf для получения календарных систем

Метод Intl.supportedValuesOf() принимает строковый параметр, указывающий, какой тип значений вернуть. Чтобы получить список календарных систем, передайте "calendar":

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// Output: ["buddhist", "chinese", "coptic", "dangi", "ethioaa",
//          "ethiopic", "gregory", "hebrew", "indian", "islamic",
//          "islamic-civil", "islamic-rgsa", "islamic-tbla",
//          "islamic-umalqura", "iso8601", "japanese", "persian", "roc"]

Метод возвращает массив строк, представляющих идентификаторы календарей. Эти идентификаторы соответствуют стандарту Unicode CLDR (Common Locale Data Repository), который обеспечивает единый способ обозначения календарных систем на разных платформах и языках программирования.

Возвращаемый массив обладает следующими характеристиками:

  • Значения отсортированы в алфавитном порядке по возрастанию
  • Дубликаты удалены
  • Каждый идентификатор записан строчными буквами и через дефис
  • Список включает все календарные системы, поддерживаемые реализацией JavaScript

Разные браузеры и среды JavaScript поддерживают разные наборы календарей, хотя все современные браузеры поддерживают основной набор распространённых календарей.

Понимание идентификаторов календарей

Каждый идентификатор календаря представляет определённую календарную систему, используемую одной или несколькими культурами. Вот самые распространённые идентификаторы:

Идентификатор "gregory" обозначает григорианский календарь — стандартный гражданский календарь, используемый в большинстве стран. Если вы живёте в США, Европе или большинстве других регионов мира, вы используете именно этот календарь в повседневной жизни.

Идентификатор "buddhist" обозначает тайский буддийский календарь, который использует ту же структуру месяцев и дней, что и григорианский, но отсчёт лет ведётся от рождения Будды (543 год до н. э. по григорианскому календарю). 2025 год по григорианскому календарю — это 2568 год по буддийскому календарю.

Идентификатор "chinese" обозначает традиционный китайский календарь — лунно-солнечный календарь, в котором месяцы следуют фазам Луны, а годы соотносятся с орбитальным периодом Юпитера. Китайский календарь используется для определения традиционных праздников, например, Китайского Нового года.

Идентификатор "islamic" обозначает исламский календарь Хиджры — полностью лунный календарь с 12 месяцами по 29 или 30 дней. Годы отсчитываются от Хиджры — переселения Мухаммеда из Мекки в Медину в 622 году н.э.

Идентификатор "hebrew" обозначает еврейский календарь — лунно-солнечный календарь, используемый для еврейских религиозных обрядов. Годы отсчитываются от традиционной даты сотворения мира (3761 год до н.э. по григорианскому календарю).

Идентификатор "japanese" обозначает японский календарь, который использует ту же структуру месяцев и дней, что и григорианский календарь, но делит время на эры в зависимости от правящего императора. Текущая эра — Рэйва, началась в 2019 году.

Идентификатор "persian" обозначает солнечный календарь Хиджры, используемый в Иране и Афганистане. Это солнечный календарь, в котором годы отсчитываются от Хиджры, что отличает его от лунного исламского календаря.

Дополнительные идентификаторы включают "coptic" (коптский православный календарь), "dangi" (традиционный корейский календарь), "ethiopic" (эфиопский календарь), "indian" (индийский национальный календарь) и "roc" (календарь Китайской Республики, используемый на Тайване).

Некоторые идентификаторы имеют варианты, такие как "islamic-civil", "islamic-rgsa", "islamic-tbla" и "islamic-umalqura", которые обозначают разные методы вычисления исламского календаря.

Идентификатор "iso8601" обозначает календарь ISO 8601, который по сути является григорианским календарём, но всегда использует пролептический григорианский календарь (распространяет григорианский календарь на даты до его введения в 1582 году).

Как работают разные календарные системы

Чтобы понять, как календарные системы влияют на форматирование дат, попробуйте отформатировать одну и ту же дату с помощью разных календарей:

const date = new Date("2025-10-15");

const gregorian = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const islamic = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const hebrew = new Intl.DateTimeFormat("en-US", {
  calendar: "hebrew",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const buddhist = new Intl.DateTimeFormat("en-US", {
  calendar: "buddhist",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(gregorian.format(date));
// Output: "October 15, 2025"

console.log(islamic.format(date));
// Output: "Rabi' II 16, 1447 AH"

console.log(hebrew.format(date));
// Output: "Tishrei 23, 5786"

console.log(buddhist.format(date));
// Output: "October 15, 2568 BE"

Один и тот же объект JavaScript Date представляет один и тот же момент времени, но каждая календарная система отображает этот момент с разными значениями года, месяца и дня. Например, григорианская дата 15 октября 2025 года соответствует 16 Раби' II 1447 года в исламском календаре, 23 тишрея 5786 года в еврейском календаре и 15 октября 2568 года в буддийском календаре.

Это показывает, почему важно указывать, какую календарную систему использовать при форматировании дат для пользователей, которые следуют разным календарям.

Создание селектора календаря

При создании интерфейса, где пользователь может выбрать предпочитаемый календарь, получите список доступных календарей и динамически создайте селектор:

function buildCalendarSelector() {
  const calendars = Intl.supportedValuesOf("calendar");
  const select = document.createElement("select");
  select.id = "calendar-selector";

  calendars.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = calendar;
    select.appendChild(option);
  });

  return select;
}

const selector = buildCalendarSelector();
document.body.appendChild(selector);

Так создаётся выпадающий список со всеми поддерживаемыми календарями. Однако технические идентификаторы вроде "gregory" и "islamic" неудобны для пользователя. Пользователям нужны понятные названия на их языке.

Отображение человеко-понятных названий календарей

API Intl.DisplayNames преобразует идентификаторы календарей в человеко-понятные названия. Используйте его для более удобного селектора:

function buildCalendarSelector(locale = "en-US") {
  const calendars = Intl.supportedValuesOf("calendar");
  const displayNames = new Intl.DisplayNames([locale], { type: "calendar" });

  const select = document.createElement("select");
  select.id = "calendar-selector";

  calendars.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = displayNames.of(calendar);
    select.appendChild(option);
  });

  return select;
}

const selector = buildCalendarSelector("en-US");
document.body.appendChild(selector);

Теперь в выпадающем списке отображаются такие названия, как «Григорианский календарь», «Исламский календарь» и «Еврейский календарь» вместо технических идентификаторов.

Вы можете показывать названия календарей на разных языках, меняя локаль:

const calendars = ["gregory", "islamic", "hebrew", "buddhist", "chinese"];

const englishNames = new Intl.DisplayNames(["en-US"], { type: "calendar" });
const frenchNames = new Intl.DisplayNames(["fr-FR"], { type: "calendar" });
const arabicNames = new Intl.DisplayNames(["ar-SA"], { type: "calendar" });

calendars.forEach(calendar => {
  console.log(`${calendar}:`);
  console.log(`  English: ${englishNames.of(calendar)}`);
  console.log(`  French: ${frenchNames.of(calendar)}`);
  console.log(`  Arabic: ${arabicNames.of(calendar)}`);
});

// Output:
// gregory:
//   English: Gregorian Calendar
//   French: calendrier grégorien
//   Arabic: التقويم الميلادي
// islamic:
//   English: Islamic Calendar
//   French: calendrier musulman
//   Arabic: التقويم الهجري
// ...

Это позволяет пользователям видеть названия календарей на своём языке.

Проверка поддержки конкретного календаря

Перед использованием календаря в приложении проверьте, поддерживается ли он в среде JavaScript. Это поможет избежать ошибок во время выполнения и реализовать резервные сценарии.

function isCalendarSupported(calendar) {
  const supported = Intl.supportedValuesOf("calendar");
  return supported.includes(calendar);
}

if (isCalendarSupported("islamic")) {
  const formatter = new Intl.DateTimeFormat("en-US", {
    calendar: "islamic",
    year: "numeric",
    month: "long",
    day: "numeric"
  });
  console.log(formatter.format(new Date()));
} else {
  console.log("Islamic calendar is not supported");
}

Этот подход работает с любым идентификатором календаря. Вы можете использовать его, чтобы показывать или скрывать опции календаря в интерфейсе пользователя или чтобы переключаться на календарь по умолчанию, если предпочтительный недоступен.

Создание переиспользуемой функции для определения поддержки

Создайте универсальную функцию для проверки поддержки календаря и корректной обработки отсутствующих календарей:

function getCalendarOrFallback(preferredCalendar, fallbackCalendar = "gregory") {
  const supported = Intl.supportedValuesOf("calendar");

  if (supported.includes(preferredCalendar)) {
    return preferredCalendar;
  }

  if (supported.includes(fallbackCalendar)) {
    return fallbackCalendar;
  }

  return supported[0];
}

const calendar = getCalendarOrFallback("islamic", "gregory");
const formatter = new Intl.DateTimeFormat("en-US", {
  calendar,
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(formatter.format(new Date()));

Эта функция пытается использовать предпочтительный календарь, если он недоступен — переключается на указанный запасной, а если оба не поддерживаются — возвращает первый доступный календарь.

Фильтрация календарей для вашего приложения

Большинству приложений не нужно поддерживать все системы календарей. Отфильтруйте список, чтобы оставить только те календари, которые актуальны для ваших пользователей:

function getRelevantCalendars(allowedCalendars) {
  const supported = Intl.supportedValuesOf("calendar");
  return allowedCalendars.filter(calendar => supported.includes(calendar));
}

const allowedCalendars = ["gregory", "islamic", "hebrew", "buddhist"];
const availableCalendars = getRelevantCalendars(allowedCalendars);

console.log(availableCalendars);
// Output: ["gregory", "islamic", "hebrew", "buddhist"]
// (assuming all are supported in the current environment)

Так вы предложите только те календари, которые соответствуют вашим требованиям и работают в браузере пользователя.

Это можно совместить с выбором календаря, чтобы сделать интерфейс более удобным:

function buildFilteredCalendarSelector(allowedCalendars, locale = "en-US") {
  const supported = Intl.supportedValuesOf("calendar");
  const available = allowedCalendars.filter(cal => supported.includes(cal));
  const displayNames = new Intl.DisplayNames([locale], { type: "calendar" });

  const select = document.createElement("select");

  available.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = displayNames.of(calendar);
    select.appendChild(option);
  });

  return select;
}

const selector = buildFilteredCalendarSelector(
  ["gregory", "islamic", "hebrew", "buddhist"],
  "en-US"
);
document.body.appendChild(selector);

Такой селектор покажет только поддерживаемые календари с понятными названиями, которые точно работают в текущей среде.

Группировка календарей по категориям

Если приложение поддерживает много календарей, сгруппируйте их по типу или региону — это повысит удобство:

function groupCalendars() {
  const calendars = Intl.supportedValuesOf("calendar");

  const groups = {
    solar: ["gregory", "iso8601", "persian", "ethiopic", "coptic"],
    lunar: ["islamic", "islamic-civil", "islamic-rgsa", "islamic-tbla", "islamic-umalqura"],
    lunisolar: ["hebrew", "chinese", "dangi"],
    erasBased: ["japanese", "roc", "buddhist"],
    other: []
  };

  const grouped = {
    solar: [],
    lunar: [],
    lunisolar: [],
    erasBased: [],
    other: []
  };

  calendars.forEach(calendar => {
    let placed = false;

    for (const [group, members] of Object.entries(groups)) {
      if (members.includes(calendar)) {
        grouped[group].push(calendar);
        placed = true;
        break;
      }
    }

    if (!placed) {
      grouped.other.push(calendar);
    }
  });

  return grouped;
}

const grouped = groupCalendars();
console.log(grouped);
// Output:
// {
//   solar: ["gregory", "iso8601", "persian", "ethiopic", "coptic"],
//   lunar: ["islamic", "islamic-civil", "islamic-rgsa", ...],
//   lunisolar: ["hebrew", "chinese", "dangi"],
//   erasBased: ["japanese", "roc", "buddhist"],
//   other: ["ethioaa", "indian"]
// }

Такая организация помогает пользователям понять особенности разных календарных систем и быстрее найти нужный вариант.

Обработка неподдерживаемых сред

Метод Intl.supportedValuesOf() появился в JavaScript в 2022 году. В старых браузерах он не поддерживается. Перед использованием проверьте, существует ли этот метод:

function getCalendars() {
  if (typeof Intl.supportedValuesOf === "function") {
    return Intl.supportedValuesOf("calendar");
  }

  return ["gregory"];
}

const calendars = getCalendars();
console.log(calendars);

Это возвращает полный список поддерживаемых календарей в современных браузерах, а в старых средах используется только григорианский календарь.

Для лучшей обратной совместимости укажите более полный список запасных вариантов:

function getCalendars() {
  if (typeof Intl.supportedValuesOf === "function") {
    return Intl.supportedValuesOf("calendar");
  }

  return [
    "buddhist",
    "chinese",
    "coptic",
    "ethiopic",
    "gregory",
    "hebrew",
    "indian",
    "islamic",
    "japanese",
    "persian",
    "roc"
  ];
}

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

Понимание разницы между календарём и локалью

Календарь и локаль — связанные, но разные понятия. Локаль определяет язык и региональные правила форматирования, а календарь — какую систему летоисчисления использовать.

Одна локаль может использовать несколько календарей. Например, арабоязычные в Саудовской Аравии обычно используют исламский календарь для религиозных целей и григорианский — для гражданских. Даты на арабском можно форматировать с любым из этих календарей:

const date = new Date("2025-10-15");

const arabicGregorian = new Intl.DateTimeFormat("ar-SA", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const arabicIslamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(arabicGregorian.format(date));
// Output: "١٥ أكتوبر ٢٠٢٥"

console.log(arabicIslamic.format(date));
// Output: "١٦ ربيع الآخر ١٤٤٧ هـ"

В обоих случаях используется арабский текст и цифры, но отображаются разные даты в зависимости от календаря.

Наоборот, один и тот же календарь можно отформатировать на разных языках:

const date = new Date("2025-10-15");

const englishIslamic = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const arabicIslamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(englishIslamic.format(date));
// Output: "Rabi' II 16, 1447 AH"

console.log(arabicIslamic.format(date));
// Output: "١٦ ربيع الآخر ١٤٤٧ هـ"

В обоих случаях показывается исламская дата, но используются разные языки и системы чисел.

Когда запрашивать доступные календари

Запрашивайте доступные календари в следующих случаях:

При создании интерфейса выбора календаря вызывайте Intl.supportedValuesOf("calendar"), чтобы заполнить варианты. Так вы покажете только те календари, которые поддерживаются в текущей среде.

При реализации функций, связанных с календарём, проверьте, поддерживается ли нужный календарь, прежде чем его использовать. Это поможет избежать ошибок и позволит корректно переключаться на альтернативные календари.

При сохранении пользовательских настроек проверяйте выбранный календарь на наличие в списке поддерживаемых. Так вы убедитесь, что настройки останутся актуальными на разных устройствах и в разных браузерах.

При миграции между средами проверьте поддержку календарей как в исходной, так и в целевой среде. Поддержка календарей может отличаться в разных версиях браузеров, Node.js и других JavaScript-окружениях.

При загрузке данных, зависящих от календаря, убедитесь, что нужный календарь доступен, прежде чем пытаться парсить или форматировать даты. Это поможет избежать ошибок во время выполнения при работе с датами в определённых календарных системах.