異なる暦法で日付をフォーマットする方法

JavaScriptのカレンダーオプションを使用して、世界中のユーザー向けにイスラム暦、ヘブライ暦、和暦、中国暦、その他の暦法で日付を表示する

はじめに

ほとんどの開発者は、日付がどこでも同じように機能すると想定しています。2025年3月15日のDateオブジェクトを作成し、フォーマットしてユーザーに表示します。しかし、この想定はユーザーが異なる暦法に従っている場合に崩れます。

多くの文化圏では、グレゴリオ暦の2025年3月15日という日付は、まったく異なる表現になります。イスラム暦に従うユーザーにとって、その同じ瞬間はヒジュラ暦1447年ラマダーン月16日に該当します。ヘブライ暦のユーザーにとっては、5785年第二アダル月14日になります。公式な文脈における日本のユーザーにとっては、令和7年3月15日と表示されます。

暦法は、社会が時間をどのように数えるかを決定します。年がいつ始まるか、月がどのように編成されるか、日付がどのように番号付けされるかを定義します。JavaScriptのIntl.DateTimeFormat APIは17種類以上の異なる暦法をサポートしており、各ユーザーの文化的および宗教的背景に応じて日付を表示できます。

このレッスンでは、暦法とは何か、なぜ存在するのか、そしてJavaScriptで異なる暦法を使用して日付をフォーマットする方法について説明します。

暦法とは

暦法は、時間を編成し数えるためのルールを定義します。各システムは、年がいつ始まるか、何か月存在するか、日がどのように番号付けされるか、そして年の番号付けの開始点となる紀元が何であるかを確立します。

国際的な文脈で広く使用されているグレゴリオ暦は、イエス・キリストの誕生と推定される時点から年を数えます。28日から31日までの長さを持つ12か月を使用し、4年ごとに閏年がある365日の年を作り出します。

他の暦体系は異なる構造と起点を使用します。イスラム暦は12の太陰月を使用し、年間354日または355日となります。ヘブライ暦は太陰月と太陽年の調整を組み合わせています。日本の暦は各天皇の治世ごとに変わる元号を使用します。

これらの体系が存在するのは、異なる文化が宗教的意義、天文観測、歴史的出来事に基づいて独自の時間追跡方法を発展させたためです。多くの文化では、宗教行事、公式文書、文化的イベントのために、グレゴリオ暦と並行して伝統的な暦を使い続けています。

アプリケーションにおける暦体系の重要性

異なる文化的背景を持つユーザーにサービスを提供するアプリケーションは、それらのユーザーが理解し期待する形式で日付を表示する必要があります。イスラム教徒ユーザー向けの礼拝時刻アプリはイスラム暦で日付を表示すべきです。ユダヤ教の宗教行事用アプリにはヘブライ暦の日付が必要です。日本の政府関連アプリケーションには和暦形式が必要です。

誤った暦体系を使用すると混乱を招き、対象ユーザーにとってアプリケーションが使用不可能になる可能性があります。イスラム教の祝日をグレゴリオ暦で表示すると、ユーザーは手動で日付を変換する必要があります。ヘブライ暦のイベントをグレゴリオ暦形式で表示すると、それらの日付の宗教的意義が不明瞭になります。

同じDateオブジェクトは、暦体系に関係なく同じ時点を表します。変わるのは、その時点を表示用にフォーマットする方法です。JavaScriptでは、複雑な変換ロジックなしで、サポートされている任意の暦体系で任意の日付をフォーマットできます。

calendarオプションの使用

Intl.DateTimeFormatコンストラクタは、日付のフォーマット時に使用する暦体系を指定するcalendarオプションを受け入れます。暦識別子を文字列値として渡します。

const date = new Date('2025-03-15');

const gregorianFormatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'gregory',
  dateStyle: 'long'
});

const islamicFormatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  dateStyle: 'long'
});

console.log(gregorianFormatter.format(date));
// Output: "March 15, 2025"

console.log(islamicFormatter.format(date));
// Output: "Ramadan 16, 1447 AH"

同じDateオブジェクトでも、暦体系に基づいて異なる形式の文字列が生成されます。グレゴリオ暦では2025年3月15日と表示されます。イスラム暦ではヒジュラ暦1447年ラマダーン月16日と表示されます。

calendarオプションはロケールとは独立して機能します。calendarオプションを適切なロケールと組み合わせることで、英語、アラビア語、フランス語、その他の言語でイスラム暦の日付を書式設定できます。

const date = new Date('2025-03-15');

const arabicFormatter = new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic',
  dateStyle: 'long'
});

const englishFormatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  dateStyle: 'long'
});

console.log(arabicFormatter.format(date));
// Output: "١٦ رمضان ١٤٤٧ هـ"

console.log(englishFormatter.format(date));
// Output: "Ramadan 16, 1447 AH"

暦体系は表示する日付を決定し、ロケールは言語と書式設定の規則を決定します。

イスラム暦での日付の書式設定

イスラム暦は29日または30日の12か月を含む太陰暦です。完全な太陰年は約354日で、太陽暦のグレゴリオ暦より10日から11日短くなります。このため、イスラム暦の日付は時間の経過とともにグレゴリオ暦を逆方向にシフトします。

JavaScriptは複数のイスラム暦の変種をサポートしています。islamic識別子はアルゴリズムベースの計算を使用します。islamic-umalqura識別子はサウジアラビアで使用されているウンム・アル=クラー暦を使用します。islamic-civil識別子は予測可能な表形式の計算を使用します。

const date = new Date('2025-03-15');

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

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

console.log(islamicFormatter.format(date));
// Output: "Ramadan 16, 1447 AH"

console.log(umalquraFormatter.format(date));
// Output: "Ramadan 16, 1447 AH"

イスラム教徒のユーザーにサービスを提供するほとんどのアプリケーションでは、これらのイスラム暦の変種のいずれも正しく機能します。それらの違いは小さく、主に宗教的行事の正確な日付を決定する際に重要になります。

ヘブライ暦での日付の書式設定

ヘブライ暦はユダヤ教の宗教的行事に使用される太陰太陽暦です。特定の年に閏月を追加することで、太陰月と太陽年を同期させます。これにより、祝日が季節と一致するように保たれます。

const date = new Date('2025-03-15');

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

console.log(hebrewFormatter.format(date));
// Output: "14 Adar II 5785"

ヘブライ暦では、ニサン、イヤル、シヴァン、タンムズなどのヘブライ語の月名を使用します。閏年には、アダル I とアダル II が含まれます。年数は、ユダヤ教の伝統に従って天地創造からの年数を表します。

ネイティブスピーカー向けにヘブライ語でヘブライ暦の日付をフォーマットできます。

const date = new Date('2025-03-15');

const hebrewFormatter = new Intl.DateTimeFormat('he-IL', {
  calendar: 'hebrew',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

console.log(hebrewFormatter.format(date));
// Output: "י״ד באדר ב׳ ה׳תשפ״ה"

ヘブライ語の出力では、数字にヘブライ文字を使用し、月名にヘブライ文字を使用します。

和暦での日付のフォーマット

和暦では、在位中の天皇に基づく元号を使用します。現在の元号である令和は、2019年5月1日に徳仁天皇が即位したときに始まりました。年数は各元号の開始から数えられます。

const date = new Date('2025-03-15');

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

console.log(japaneseFormatter.format(date));
// Output: "March 15, 7 Reiwa"

年は令和7年と表示され、令和時代の7年目を示します。この形式は、日本の公式文書、政府の書式、正式な文脈で使用されます。

日本語でのフォーマットは、伝統的な日本の日付形式を生成します。

const date = new Date('2025-03-15');

const japaneseFormatter = new Intl.DateTimeFormat('ja-JP', {
  calendar: 'japanese',
  era: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

console.log(japaneseFormatter.format(date));
// Output: "令和7年3月15日"

出力には、元号(令和)、年(7年)、月(3月)、日(15日)が日本語の文字で表示されます。

中国暦での日付のフォーマット

中国暦は、春節や中秋節などの伝統的な中国の祝日を決定するために使用される太陰太陽暦です。この暦は、太陰月と二十四節気を組み合わせています。

const date = new Date('2025-03-15');

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

console.log(chineseFormatter.format(date));
// Output: "Second Month 16, 2025(yi-si)"

中国暦の年には、数字の年とともに干支名(この場合は乙巳)が含まれます。月名は「正月」や「二月」などの数字による呼称を使用します。

中国語でのフォーマットは、中国語の文字を使用して日付を表示します。

const date = new Date('2025-03-15');

const chineseFormatter = new Intl.DateTimeFormat('zh-CN', {
  calendar: 'chinese',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

console.log(chineseFormatter.format(date));
// Output: "2025乙巳年二月十六"

ペルシア暦での日付のフォーマット

ペルシア暦は太陽ヒジュラ暦とも呼ばれ、イランとアフガニスタンの公式暦です。グレゴリオ暦と構造が似た12か月の太陽年を使用していますが、月の長さと紀元が異なります。

const date = new Date('2025-03-15');

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

console.log(persianFormatter.format(date));
// Output: "Esfand 24, 1403 AP"

ペルシア暦1403年はグレゴリオ暦2025年に相当します。APはAnno Persicoの略です。

ペルシア語の書式設定では、ペルシア数字と月名を使用します。

const date = new Date('2025-03-15');

const persianFormatter = new Intl.DateTimeFormat('fa-IR', {
  calendar: 'persian',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

console.log(persianFormatter.format(date));
// Output: "۲۴ اسفند ۱۴۰۳ ه‍.ش."

その他のサポートされている暦システム

JavaScriptは、さまざまな文化的および宗教的な文脈に対応した追加の暦システムをサポートしています。

buddhist暦は、グレゴリオ暦に543年を加えたもので、タイやその他の上座部仏教国で使用されています。

const date = new Date('2025-03-15');

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

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

indian暦は、インドの公式民間暦です。

const date = new Date('2025-03-15');

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

console.log(indianFormatter.format(date));
// Output: "Phalguna 24, 1946 Saka"

coptic暦は、コプト正教会で使用されています。ethiopic暦は、エチオピアで使用されています。

const date = new Date('2025-03-15');

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

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

console.log(copticFormatter.format(date));
// Output: "Amshir 6, 1741 ERA1"

console.log(ethiopicFormatter.format(date));
// Output: "Yekatit 6, 2017 ERA1"

サポートされている暦の確認

Intl.supportedValuesOf()メソッドは、JavaScript実装でサポートされているすべての暦識別子の配列を返します。

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

正確なリストは、JavaScriptエンジンとブラウザのバージョンによって異なります。このメソッドは常に暦をアルファベット順に返します。

特定の暦を使用する前に、それがサポートされているかどうかを確認できます。

const calendars = Intl.supportedValuesOf('calendar');
const supportsIslamic = calendars.includes('islamic');

if (supportsIslamic) {
  const formatter = new Intl.DateTimeFormat('en-US', {
    calendar: 'islamic',
    dateStyle: 'long'
  });
  console.log(formatter.format(new Date()));
}

このチェックにより、すべての暦システムをサポートしていない可能性のある環境でのエラーを防ぐことができます。

Unicode拡張による暦の指定

オプションパラメータを使用する代わりに、ロケール識別子のUnicode拡張キーを使用して暦システムを指定できます。ロケール文字列に-u-ca-と暦識別子を追加します。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat('en-US-u-ca-islamic', {
  dateStyle: 'long'
});

console.log(formatter.format(date));
// Output: "Ramadan 16, 1447 AH"

-u-ca-islamic拡張子は、フォーマッターにイスラム暦を使用するよう指示します。これは、オプションオブジェクトにcalendar: 'islamic'を渡すのと同じ結果を生成します。

ロケール文字列とオプションオブジェクトの両方で暦を指定した場合、オプションオブジェクトが優先されます。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat('en-US-u-ca-islamic', {
  calendar: 'hebrew',
  dateStyle: 'long'
});

console.log(formatter.format(date));
// Output: "14 Adar II 5785"

オプションオブジェクトのヘブライ暦が、ロケール文字列のイスラム暦を上書きします。暦システムをプログラム的に制御する必要がある場合は、optionsパラメータを使用してください。ユーザー設定や構成からのロケール識別子を扱う場合は、Unicode拡張を使用してください。

ロケールがデフォルトの暦を決定する方法

暦を指定しない場合、フォーマッターはそのロケールのデフォルトの暦を使用します。ほとんどのロケールはグレゴリオ暦をデフォルトとしますが、一部のロケールは異なるデフォルトを使用します。

const date = new Date('2025-03-15');

const usFormatter = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'long'
});

const saFormatter = new Intl.DateTimeFormat('ar-SA', {
  dateStyle: 'long'
});

const ilFormatter = new Intl.DateTimeFormat('he-IL', {
  dateStyle: 'long'
});

console.log(usFormatter.format(date));
// Output: "March 15, 2025"

console.log(saFormatter.format(date));
// Output: "١٦ رمضان ١٤٤٧ هـ"

console.log(ilFormatter.format(date));
// Output: "15 במרץ 2025"

米国英語ロケールはデフォルトでグレゴリオ暦を使用します。サウジアラビアのアラビア語ロケールはデフォルトでイスラム暦を使用します。イスラエルのヘブライ語ロケールは、強いヘブライ暦の伝統があるにもかかわらず、デフォルトでグレゴリオ暦を使用します。

フォーマッターが使用している暦は、resolvedOptions()メソッドを呼び出すことで確認できます。

const formatter = new Intl.DateTimeFormat('ar-SA', {
  dateStyle: 'long'
});

const options = formatter.resolvedOptions();
console.log(options.calendar);
// Output: "islamic-umalqura"

解決されたオプションは、ar-SAロケールがデフォルトでislamic-umalqura暦バリアントを使用することを示しています。

暦を明示的に設定するタイミング

ユーザーのネイティブロケールで一般的な表示のために日付をフォーマットする場合は、ロケールに暦システムを決定させてください。サウジアラビアのユーザーはイスラム暦の日付を期待します。日本のユーザーは公式な文脈で和暦フォーマットを期待します。ロケールのデフォルトは、これらの期待を自動的に処理します。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat(navigator.language, {
  dateStyle: 'long'
});

console.log(formatter.format(date));
// Output varies by user's locale and default calendar

ユーザーのロケールに関係なく、特定の暦体系で日付を表示する必要がある場合は、明示的に暦を設定してください。礼拝時刻アプリは常にイスラム暦の日付を表示する必要があります。ヘブライ暦アプリは常にヘブライ暦の日付を表示する必要があります。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat(navigator.language, {
  calendar: 'islamic',
  dateStyle: 'long'
});

console.log(formatter.format(date));
// Output shows Islamic calendar date in user's language

これにより、ユーザーの言語設定を尊重しながら、暦体系がアプリケーションの目的と一致することが保証されます。

ロケールのデフォルトとは異なる暦体系で日付を表示する場合は、明示的に暦を設定してください。ヘブライ語ユーザーにグレゴリオ暦の日付を表示したり、英語話者にイスラム暦の日付を表示したりする場合があります。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'hebrew',
  dateStyle: 'long'
});

console.log(formatter.format(date));
// Output: "14 Adar II 5785"

暦と他のオプションの組み合わせ

暦オプションは、Intl.DateTimeFormatの他のすべてのオプションと連携します。dateStyletimeStyleweekdaymonthなどのコンポーネントオプション、およびtimeZoneなどの他のオプションと組み合わせることができます。

const date = new Date('2025-03-15T14:30:00');

const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  dateStyle: 'full',
  timeStyle: 'long',
  timeZone: 'America/New_York'
});

console.log(formatter.format(date));
// Output: "Saturday, Ramadan 16, 1447 AH at 2:30:00 PM EST"

フォーマッターは、完全な日付スタイル、長い時刻スタイル、および指定されたタイムゾーンを適用しながら、日付にイスラム暦を使用します。

個別のコンポーネントオプションと暦オプションを組み合わせて使用することもできます。

const date = new Date('2025-03-15');

const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'japanese',
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  era: 'long'
});

console.log(formatter.format(date));
// Output: "Saturday, March 15, 7 Reiwa"

元号オプションは、元号名を使用する日本暦や仏暦などの暦を使用する場合に特に重要になります。

異なる暦での日付範囲のフォーマット

formatRange()メソッドは、暦オプションと連携して、任意の暦体系で日付範囲をフォーマットします。

const startDate = new Date('2025-03-15');
const endDate = new Date('2025-03-25');

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

console.log(formatter.formatRange(startDate, endDate));
// Output: "Ramadan 16 – 26, 1447 AH"

フォーマッターは両方の日付にイスラム暦を適用し、繰り返される情報を賢く省略して範囲としてフォーマットします。

パフォーマンスのためのフォーマッターの再利用

Intl.DateTimeFormatインスタンスの作成には、ロケールデータと暦体系情報の処理が含まれます。同じ暦とロケールで複数の日付をフォーマットする場合は、フォーマッターを一度作成して再利用してください。

const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  dateStyle: 'long'
});

const dates = [
  new Date('2025-03-15'),
  new Date('2025-04-20'),
  new Date('2025-06-10')
];

dates.forEach(date => {
  console.log(formatter.format(date));
});
// Output:
// "Ramadan 16, 1447 AH"
// "Dhuʻl-Qiʻdah 22, 1447 AH"
// "Dhuʻl-Hijjah 15, 1447 AH"

このアプローチは、日付の配列をフォーマットする際や、アプリケーション内で多数のタイムスタンプを表示する際のパフォーマンスを向上させます。

覚えておくべきこと

暦法は、文化がどのように時間を整理し、数えるかを定義します。JavaScriptは、Intl.DateTimeFormat APIを通じて、イスラム暦、ヘブライ暦、和暦、中国暦、ペルシア暦、仏暦、コプト暦を含む17種類以上の異なる暦法をサポートしています。

オプションオブジェクトのcalendarオプションを使用するか、ロケール文字列にUnicode拡張を追加することで暦法を指定します。calendarオプションは日付をフォーマットする暦法を決定し、ロケールは言語とフォーマット規則を決定します。

一般的な用途で日付を表示する場合は、ロケールにデフォルトの暦法を決定させます。ユーザーのロケール設定に関係なく、アプリケーションが特定の暦法を必要とする場合は、明示的に暦法を設定します。

calendarオプションは、スタイル、コンポーネントオプション、タイムゾーン、日付範囲を含む他のすべての日付フォーマットオプションと連携します。パフォーマンスを向上させるため、複数の日付をフォーマットする際はフォーマッターインスタンスを再利用します。