如何在不同的日历系统中格式化日期?

使用 JavaScript 的日历选项,为全球用户显示伊斯兰历、希伯来历、日本历、农历等日历系统的日期

介绍

大多数开发者认为日期的处理方式在全球范围内都是相同的。你可以为 2025 年 3 月 15 日创建一个 Date 对象,对其进行格式化并显示给用户。然而,当用户使用不同的日历系统时,这种假设就不成立了。

在许多文化中,公历的 2025 年 3 月 15 日在其他日历系统中有完全不同的表示方式。对于使用伊斯兰历的用户来说,同一时刻是 1447 年斋月 16 日。对于使用希伯来历的用户来说,则是 5785 年亚达月 II 的第 14 天。对于在正式场合使用日本历的用户来说,则是令和 7 年 3 月 15 日。

日历系统决定了社会如何计时。它们定义了年份的起始时间、月份的组织方式以及日期的编号方式。JavaScript 的 Intl.DateTimeFormat API 支持超过 17 种不同的日历系统,允许你根据每个用户的文化和宗教背景显示日期。

本课程将解释什么是日历系统、它们为何存在,以及如何在 JavaScript 中使用不同的日历系统格式化日期。

什么是日历系统

日历系统定义了组织和计时的规则。每个系统都规定了年份的起始时间、月份的数量、日期的编号方式以及用于年份编号的起始纪元。

广泛用于国际场合的公历以耶稣基督的估计出生年份为起点。它使用 12 个月,每个月的天数从 28 天到 31 天不等,形成一个 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));
// 输出: "March 15, 2025"

console.log(islamicFormatter.format(date));
// 输出: "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));
// 输出: "١٦ رمضان ١٤٤٧ هـ"

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

日历系统决定显示哪个日期,而语言环境决定语言和格式化约定。

使用伊斯兰历格式化日期

伊斯兰历是一种包含 12 个月的阴历,每个月有 29 或 30 天。完整的阴历年大约为 354 天,比阳历(公历)短 10 到 11 天。这导致伊斯兰日期随着时间的推移在公历中向后移动。

JavaScript 支持多种伊斯兰历变体。islamic 标识符使用基于算法的计算。islamic-umalqura 标识符使用沙特阿拉伯使用的 Umm al-Qura 历法。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));
// 输出: "Ramadan 16, 1447 AH"

console.log(umalquraFormatter.format(date));
// 输出: "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));
// 输出: "14 Adar II 5785"

希伯来历使用希伯来月份名称,例如 Nisan、Iyar、Sivan 和 Tammuz。在闰年中,日历包括 Adar I 和 Adar 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));
// 输出: "י״ד באדר ב׳ ה׳תשפ״ה"

希伯来语言输出使用希伯来字母表示数字,并使用希伯来文字母表示月份名称。

使用日本历格式化日期

日本历使用基于在位天皇的年号。当前的年号是令和,从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));
// 输出: "March 15, 7 Reiwa"

年份显示为7 Reiwa,表示令和时代的第七年。这种格式用于日本的官方文件、政府表格和正式场合。

在日语中格式化会生成传统的日本日期格式。

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));
// 输出: "令和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));
// 输出: "Second Month 16, 2025(yi-si)"

中国历年份包括一个循环名称(此处为 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));
// 输出: "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));
// 输出: "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));
// 输出: "۲۴ اسفند ۱۴۰۳ ه‍.ش."

其他支持的历法系统

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));
// 输出: "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));
// 输出: "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));
// 输出: "Amshir 6, 1741 ERA1"

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

发现支持的日历

Intl.supportedValuesOf() 方法返回一个数组,其中包含 JavaScript 实现支持的所有日历标识符。

const calendars = Intl.supportedValuesOf('calendar');
console.log(calendars);
// 输出: ["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));
// 输出: "1447 年斋月 16 日"

-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));
// 输出: "5785 年阿达尔二月 14 日"

选项对象中的希伯来日历覆盖了区域字符串中的伊斯兰日历。当您需要以编程方式控制日历系统时,请使用选项参数。当使用来自用户偏好或配置的区域标识符时,请使用 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));
// 输出: "March 15, 2025"

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

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

美国英语语言环境默认使用公历。沙特阿拉伯阿拉伯语语言环境默认使用伊斯兰历。以色列希伯来语语言环境尽管有强烈的希伯来历传统,但默认使用公历。

您可以通过调用 resolvedOptions() 方法来发现格式化器正在使用的日历。

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

const options = formatter.resolvedOptions();
console.log(options.calendar);
// 输出: "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));
// 输出因用户的语言环境和默认日历而异

当您需要显示特定日历系统的日期而不考虑用户的语言环境时,请显式设置日历。例如,祷告时间应用程序应始终显示伊斯兰历日期。希伯来历应用程序应始终显示希伯来历日期。

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

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

console.log(formatter.format(date));
// 输出以用户语言显示的伊斯兰历日期

这确保了日历系统与您的应用程序目的相匹配,同时仍然尊重用户的语言偏好。

在显示与语言环境默认设置不同的日历系统的日期时,请显式设置日历。例如,您可能希望向希伯来语用户显示公历日期,或向英语用户显示伊斯兰历日期。

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

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

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

将日历与其他选项结合使用

日历选项可以与所有其他 Intl.DateTimeFormat 选项一起使用。您可以将其与 dateStyletimeStyle、组件选项(如 weekdaymonth)以及其他选项(如 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));
// 输出: "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));
// 输出: "Saturday, March 15, 7 Reiwa"

当使用如日本或佛教等使用纪元名称的日历时,era 选项显得尤为重要。

使用不同日历格式化日期范围

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));
// 输出: "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));
});
// 输出:
// "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 选项可以与所有其他日期格式化选项一起使用,包括样式、组件选项、时区和日期范围。在格式化多个日期时,重用格式化器实例以获得更好的性能。