利用可能なカレンダーシステムの一覧を取得する

JavaScriptの環境がサポートしているカレンダーシステムを確認する

はじめに

グローバルな利用者向けにアプリケーションを構築する場合、ユーザーは多くの場合、自分の好みのカレンダーシステムで日付を表示する必要があります。ほとんどの西洋諸国で使用されているグレゴリオ暦には馴染みがあるかもしれませんが、多くの文化では、イスラム暦(ヒジュラ暦)、ヘブライ暦、仏教暦など、まったく異なるカレンダーシステムを使用しています。

ユーザーが好みのカレンダーを選択できるようにするには、JavaScriptの環境がどのカレンダーシステムをサポートしているかを知る必要があります。時代遅れになったり、サポートされていない値を含んだりするハードコードされたリストを維持するのではなく、JavaScriptは実行時に利用可能なカレンダーシステムを発見するメソッドを提供しています。

"calendar"パラメータを持つIntl.supportedValuesOf()メソッドは、現在の環境でサポートされているすべてのカレンダーシステム識別子の配列を返します。これにより、アプリケーションが正しく機能するカレンダーオプションのみを提供することが保証されます。

カレンダーシステムとは

カレンダーシステムは、時間を整理し計算するための異なる方法です。すべてのカレンダーは日、月、年を追跡しますが、年がいつ始まるか、月がどれくらい続くか、そして天文学的な周期をどのように考慮するかについて異なるルールを使用しています。

1582年に導入されたグレゴリオ暦は、最も広く使用されている市民カレンダーです。約365.25日の太陽年を使用し、12ヶ月に分割され、4年ごとに閏年が追加されます(世紀年には例外があります)。

他のカレンダーシステムは異なるルールに従います。イスラム暦は純粋に月の周期に基づいており、月は月の満ち欠けに従います。これはイスラム暦の年が太陽年より約11日短いことを意味し、イスラム暦の日付が時間とともに季節を通じて移動する原因となります。ヘブライ暦と中国暦は太陰太陽暦で、太陽年と同期を保つための調整と共に月の周期を組み合わせています。

JavaScriptで日付をフォーマットする場合、カレンダーシステムによって表示される年、月、日の数字が決まります。同じ時点でも、異なるカレンダーシステムでは異なる日付表現になります。

Intl.supportedValuesOf を使用して暦システムを取得する

Intl.supportedValuesOf() メソッドは、返す値の種類を指定する文字列パラメータを受け取ります。暦システムを取得するには、"calendar" を渡します:

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

このメソッドは暦識別子を表す文字列の配列を返します。これらの識別子はUnicode CLDR(共通ロケールデータリポジトリ)標準に従っており、異なるプラットフォームやプログラミング言語間で暦システムを一貫した方法で参照できるようにしています。

返される配列には以下の特徴があります:

  • 値はアルファベット昇順でソートされています
  • 重複値は削除されています
  • 各識別子は小文字とハイフンを使用しています
  • リストにはJavaScript実装でサポートされているすべての暦システムが含まれています

異なるブラウザやJavaScript環境では、サポートされる暦のセットが異なりますが、すべての最新ブラウザは一般的な暦の基本セットをサポートしています。

暦識別子を理解する

各暦識別子は、一つまたは複数の文化で使用される特定の暦システムを表しています。以下が最も一般的な識別子です:

"gregory" 識別子はグレゴリオ暦を表し、ほとんどの国で使用される標準的な市民暦です。アメリカ、ヨーロッパ、または世界のほとんどの地域に住んでいる場合、これは日常生活で使用している暦です。

"buddhist" 識別子はタイ仏教暦を表し、グレゴリオ暦と同じ月と日の構造を使用していますが、年はブッダの誕生(グレゴリオ暦では紀元前543年)から数えます。グレゴリオ暦の2025年は仏教暦では2568年です。

"chinese" 識別子は伝統的な中国暦を表し、太陰太陽暦で月は月の満ち欠けに従い、年は木星の公転周期に合わせています。中国暦は旧正月などの伝統的な祝日を決定するために使用されます。

"islamic" 識別子はイスラム暦(ヒジュラ暦)を表し、29日または30日の12ヶ月からなる純粋な太陰暦です。年はヒジュラ(ムハンマドがメッカからメディナに移住した西暦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));
// 出力: "October 15, 2025"

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

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

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

同じJavaScript Dateオブジェクトは同じ時点を表していますが、各カレンダーシステムはその時点を異なる年、月、日の値で表現します。グレゴリオ暦の2025年10月15日は、イスラム暦ではラビー・アル=サーニー16日、1447年、ヘブライ暦ではティシュレイ23日、5786年、仏教暦では2568年10月15日に相当します。

これは、異なるカレンダーを使用するユーザー向けに日付をフォーマットする際に、どのカレンダーシステムを使用するかを指定する必要がある理由を示しています。

カレンダーセレクターの構築

ユーザーが好みのカレンダーを選択できるユーザーインターフェースを作成する場合、利用可能なカレンダーを照会して動的にセレクターを構築します:

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"などの技術的な識別子はユーザーフレンドリーではありません。ユーザーは自分の言語で説明的な名前を見る必要があります。

人間が読みやすいカレンダー名の表示

Intl.DisplayNames APIはカレンダー識別子を人間が読みやすい名前に変換します。これを使用して、より良いカレンダーセレクターを作成できます:

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)}`);
});

// 出力:
// 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);
// 出力: ["gregory", "islamic", "hebrew", "buddhist"]
// (現在の環境ですべてがサポートされていると仮定)

これにより、要件を満たし、ユーザーのブラウザで動作するカレンダーのみを提供することができます。

これをカレンダー選択と組み合わせて、焦点を絞ったユーザーインターフェースを作成できます:

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);
// 出力:
// {
//   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()メソッドは2022年にJavaScriptに追加されました。古いブラウザではサポートされていません。使用する前にこのメソッドが存在するかどうかを確認してください:

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"
  ];
}

これにより、古いブラウザに対して妥当なカレンダーセットが提供されますが、すべての環境ですべてのカレンダーが機能することは保証できません。

カレンダーとロケールの違いを理解する

カレンダーとロケールは関連していますが、別々の概念です。ロケールは言語や地域の書式設定規則を決定し、カレンダーはどの暦システムを使用するかを決定します。

1つのロケールで複数のカレンダーを使用することができます。例えば、サウジアラビアのアラビア語話者は、宗教的な目的にはイスラム暦を、市民的な目的にはグレゴリオ暦を使用するのが一般的です。アラビア語での日付は、どちらのカレンダーでもフォーマットできます:

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));
// 出力: "١٥ أكتوبر ٢٠٢٥"

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

両方ともアラビア語のテキストと数字を使用していますが、それぞれのカレンダーに従って異なる日付を表示します。

逆に、同じカレンダーを異なる言語でフォーマットすることもできます:

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));
// 出力: "Rabi' II 16, 1447 AH"

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

両方ともイスラム暦の日付を表示していますが、異なる言語と数字体系を使用しています。

利用可能なカレンダーを照会するタイミング

以下の状況で利用可能なカレンダーを照会してください:

カレンダー選択インターフェースを構築する際は、Intl.supportedValuesOf("calendar")を呼び出してオプションを取得します。これにより、現在の環境で動作するカレンダーのみが表示されます。

カレンダーベースの機能を実装する際は、必要なカレンダーが使用前にサポートされているかを確認します。これによりエラーを防ぎ、代替カレンダーへの適切なフォールバックが可能になります。

ユーザー設定を保存する際は、カレンダーの選択をサポートされているリストと照合して検証します。これにより、保存された設定が異なるデバイスやブラウザ間で有効であることが保証されます。

環境間で移行する際は、ソース環境とターゲット環境の両方でカレンダーのサポートを確認します。カレンダーのサポートは、ブラウザのバージョン、Node.jsのバージョン、異なるJavaScriptランタイム間で異なる場合があります。

カレンダーに依存するデータを読み込む際は、特定のカレンダーシステムで日付を解析またはフォーマットする前に、そのカレンダーが利用可能であることを確認します。これにより、実行時エラーを防止できます。