如何在非公历日历中显示年份和纪元?

使用 Intl.DateTimeFormat 中的 calendar 和 era 选项显示伊斯兰历、希伯来历、中国农历、波斯历等日历系统的年份和纪元

介绍

公历从一个起点开始计年,因此 2024 年是一个简单的数字。而其他历法系统的计年方式有所不同。伊斯兰历从公元 622 年开始计年。希伯来历从数千年前的传统创世日期开始计年。中国农历使用一个 60 年的周期来命名年份,而不是连续的数字。

这些不同的计年系统意味着同一时刻在不同的历法中有不同的年份表示。例如,公历 2024 年 10 月 15 日在伊斯兰历中是 1446 年,在希伯来历中是 5785 年,在中国农历中是 2024 年(甲辰,jiǎ-chén)。

JavaScript 的 Intl.DateTimeFormat 提供了选项,可以显示任何历法系统的年份和纪元。calendar 选项用于选择使用的历法系统,yearera 选项控制年份和纪元的显示方式。对于使用命名年份而非数字的历法,formatToParts() 方法可以同时获取年份名称及其对应的公历年份。

不同历法系统中的年份差异

历法系统在三个基本方面存在差异:起始计年的时间点、年份的编号方式以及是否使用纪元。

公历从公元 1 年开始计年,并按顺序递增。伊斯兰历从公元 622 年的 1 AH(希吉拉后)开始计年。希伯来历从公元前 3761 年的 1 年开始计年。每种历法都有其自己的纪元,即计年开始的时间点。

一些历法使用永远递增的连续年份编号,例如公历、伊斯兰历、希伯来历和波斯历。其他历法则使用周期性年份名称,例如中国农历使用一个 60 年的周期命名年份。60 年后,周期会重新开始。

纪元将时间划分为命名的时期。公历使用公元前(BC)和公元后(AD)。日本历法使用天皇年号。伊斯兰历和希伯来历通常使用从其纪元开始的单一纪元。中国农历则不以相同方式使用纪元,而是依赖于周期内的命名年份。

在伊斯兰历中显示年份

伊斯兰历以希吉拉纪年,即穆罕默德在公元 622 年从麦加迁徙到麦地那为起点。伊斯兰历的第 1 年对应公历的 622 年。伊斯兰历使用阴历月份,因此其年份比公历年份短。这意味着伊斯兰历年份推进得更快,目前在公历 2024 年时已达到 1446 年。

您可以通过在 calendar 选项中指定值 islamic,或在区域标识符中添加 Unicode 扩展 -u-ca-islamic 来指定伊斯兰历。

const date = new Date('2024-10-15');

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

console.log(formatter.format(date));
// "Rabi' II 12, 1446 AH"

年份显示为 1446,这是对应于 2024 年 10 月 15 日的伊斯兰历年份。时代标识 "AH"(希吉拉后)在显示英文伊斯兰日期时会自动出现。

不同的区域会根据其惯例格式化伊斯兰日期。

const date = new Date('2024-10-15');

const en = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(en.format(date));
// "Rabi' II 12, 1446 AH"

const ar = new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(ar.format(date));
// "١٢ ربيع الآخر ١٤٤٦ هـ"

阿拉伯语区域使用阿拉伯-印度数字,并以阿拉伯语显示月份名称。时代标识更改为 "هـ"(希吉拉的阿拉伯语缩写)。

控制伊斯兰历中的时代显示

era 选项控制是否以及如何显示时代标识。该选项接受三个值:long 表示完整的时代名称,short 表示缩写,narrow 表示最紧凑的形式。

const date = new Date('2024-10-15');

const long = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  era: 'long'
});
console.log(long.format(date));
// "1446 Anno Hegirae"

const short = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  era: 'short'
});
console.log(short.format(date));
// "1446 AH"

const narrow = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  era: 'narrow'
});
console.log(narrow.format(date));
// "1446 A"

long 值生成 "Anno Hegirae"(拉丁语,意为“希吉拉之年”)。short 值生成 "AH"。narrow 值仅生成 "A"。

伊斯兰历的变体

伊斯兰历有多个变体,使用不同的计算方法。JavaScript 支持五种变体:islamicislamic-civilislamic-tblaislamic-umalquraislamic-rgsa

islamic-umalqura 变体使用沙特阿拉伯的官方历法,该历法基于天文观测。islamic-civil 变体使用固定的算术计算方法,交替采用 29 天和 30 天的月份。

const date = new Date('2024-10-15');

const umalqura = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic-umalqura',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(umalqura.format(date));
// "Rabi' II 12, 1446 AH"

const civil = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic-civil',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(civil.format(date));
// "Rabi' II 11, 1446 AH"

由于这些变体使用不同的计算方法来确定月份的边界,因此它们可能会为同一公历日期生成不同的日期。

在希伯来历中显示年份

希伯来历从公元前 3761 年的传统创世日期开始计年。这使得希伯来年的年份比公历年份大得多。希伯来年 5785 对应于公历的 2024 年。

您可以通过将 calendar 选项设置为 hebrew 或在区域标识符中添加 Unicode 扩展 -u-ca-hebrew 来指定希伯来历。

const date = new Date('2024-10-15');

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

console.log(formatter.format(date));
// "Tishrei 13, 5785"

年份显示为 5785,这是 2024 年 10 月 15 日对应的希伯来年。希伯来历是一个阴阳历,这意味着它的月份遵循月亮,但通过周期性闰月使年份与太阳年保持同步。

在希伯来区域中,希伯来日期使用希伯来数字和月份名称。

const date = new Date('2024-10-15');

const he = new Intl.DateTimeFormat('he-IL', {
  calendar: 'hebrew',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(he.format(date));
// "י״ג בתשרי ה׳תשפ״ה"

希伯来区域会完全以希伯来语显示日期,包括用作数字的希伯来字母。

在中国历中显示年份

中国历使用一个60年的周期来命名年份,而不是连续的年份数字。周期中的每一年都有一个由两个字符组成的名称:一个天干和一个地支。2024年是甲辰年,这在传统的中国占星术中表示“木龙”。

由于中国历使用命名年份,因此显示它需要与使用连续年份数字的日历不同的处理方式。

const date = new Date('2024-10-15');

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

console.log(formatter.format(date));
// "Ninth Month 13, 2024(jiǎ-chén)"

格式化的输出包括公历年份2024和括号中的年份名称“jiǎ-chén”。这种双重表示有助于用户理解周期性年份名称和对应的公历年份。

在中文区域设置中,中国日期以汉字显示。

const date = new Date('2024-10-15');

const zh = new Intl.DateTimeFormat('zh-CN', {
  calendar: 'chinese',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(zh.format(date));
// "2024甲辰年九月十三"

中文区域设置使用汉字显示年份名称。格式将年份名称自然地集成到日期字符串中。

提取年份名称和相关年份

使用命名年份的日历提供了两部分信息:周期内的年份名称和对应的公历年份。formatToParts()方法将这些信息分为不同的部分。

const date = new Date('2024-10-15');

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

const parts = formatter.formatToParts(date);
console.log(parts);

parts数组包含两个相关的条目:

[
  { type: 'relatedYear', value: '2024' },
  { type: 'yearName', value: '甲辰' },
  // ... 其他部分
]

relatedYear部分包含四位数的公历年份。yearName部分包含周期性年份名称。这种分离允许您在自定义格式中使用其中一个或两个值。

您可以提取这些部分来创建自定义日期显示。

const date = new Date('2024-10-15');

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

const parts = formatter.formatToParts(date);
const yearName = parts.find(p => p.type === 'yearName')?.value;
const relatedYear = parts.find(p => p.type === 'relatedYear')?.value;

console.log(`Year name: ${yearName}`);
// "Year name: 甲辰"

console.log(`Gregorian year: ${relatedYear}`);
// "Gregorian year: 2024"

此技术适用于任何使用命名年份或周期的日历。

在波斯历中显示年份

波斯历,也称为太阳历(Solar Hijri calendar),从公元 622 年的希吉拉纪元开始计年,与伊斯兰历的纪元相同。然而,波斯历使用太阳月而非阴历月,因此在结构上更类似于公历。

您可以通过将 calendar 选项设置为 persian 或在区域标识符中添加 Unicode 扩展 -u-ca-persian 来指定波斯历。

const date = new Date('2024-10-15');

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

console.log(formatter.format(date));
// "Mehr 24, 1403 AP"

年份显示为 1403,这是 2024 年 10 月 15 日对应的波斯年。纪元 "AP"(Anno Persico)出现在英文格式中。

波斯区域的波斯日期使用波斯数字和月份名称。

const date = new Date('2024-10-15');

const fa = new Intl.DateTimeFormat('fa-IR', {
  calendar: 'persian',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(fa.format(date));
// "۲۴ مهر ۱۴۰۳ ه‍.ش."

波斯区域使用波斯(法尔西)字符和数字显示日期。纪元指示符 "ه‍.ش." 是太阳历纪元的波斯语缩写。

结合日历和纪元选项

您可以结合使用 calendarera 选项,以精确控制不同日历系统中年份和纪元的显示方式。这种组合使您能够精确控制日期格式。

const date = new Date('2024-10-15');

const gregorian = new Intl.DateTimeFormat('en-US', {
  calendar: 'gregory',
  year: 'numeric',
  month: 'long',
  era: 'short'
});
console.log(gregorian.format(date));
// "October 2024 AD"

const islamic = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  era: 'short'
});
console.log(islamic.format(date));
// "Rabi' II 1446 AH"

const hebrew = new Intl.DateTimeFormat('en-US', {
  calendar: 'hebrew',
  year: 'numeric',
  month: 'long',
  era: 'short'
});
console.log(hebrew.format(date));
// "Tishrei 5785"

由于设置了 era 选项,公历和伊斯兰历日期显示了它们的纪元指示符。而希伯来历日期在此格式中未显示纪元指示符,因为希伯来历格式通常省略它。

您还可以混合使用区域和日历,以一种语言显示另一种文化的日历日期。

const date = new Date('2024-10-15');

const englishIslamic = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(englishIslamic.format(date));
// "Rabi' II 12, 1446 AH"

const arabicIslamic = new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
console.log(arabicIslamic.format(date));
// "١٢ ربيع الآخر ١٤٤٦ هـ"

两个格式化器都使用伊斯兰历,但英文区域生成英文月份名称和拉丁数字,而阿拉伯区域生成阿拉伯月份名称和阿拉伯-印度数字。

在多个日历中显示年份

为国际用户服务的应用程序通常需要在多个日历系统中显示相同的日期。您可以创建多个格式化器来显示并行表示。

const date = new Date('2024-10-15');

const calendars = [
  { name: 'Gregorian', calendar: 'gregory' },
  { name: 'Islamic', calendar: 'islamic' },
  { name: 'Hebrew', calendar: 'hebrew' },
  { name: 'Persian', calendar: 'persian' }
];

calendars.forEach(cal => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    calendar: cal.calendar,
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });
  console.log(`${cal.name}: ${formatter.format(date)}`);
});

这将生成:

Gregorian: October 15, 2024
Islamic: Rabi' II 12, 1446 AH
Hebrew: Tishrei 13, 5785
Persian: Mehr 24, 1403 AP

每个日历使用其自己的年份计数系统和纪元指示符表示同一时刻。

常见用例

国际化应用程序需要以用户期望的日历系统显示日期。一个伊斯兰银行应用程序会使用伊斯兰日历显示交易日期。

const transactionDate = new Date('2024-10-15');

const formatter = new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic-umalqura',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  era: 'short'
});

console.log(`Transaction date: ${formatter.format(transactionDate)}`);
// "Transaction date: ١٢ ربيع الآخر ١٤٤٦ هـ"

宗教日历决定节假日和纪念日的日期。一个犹太日历应用程序会为节假日显示希伯来日期。

const roshHashanah2024 = new Date('2024-10-03');

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

console.log(`Rosh Hashanah: ${formatter.format(roshHashanah2024)}`);
// "Rosh Hashanah: א׳ בתשרי ה׳תשפ״ה"

历史应用程序会以当时使用的日历系统显示日期。一个关于古代波斯的应用程序会使用波斯日历显示日期。

const historicalDate = new Date('2024-03-20');

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

console.log(`Persian New Year: ${formatter.format(historicalDate)}`);
// "Persian New Year: Farvardin 1, 1403 Anno Persico"

摘要

非公历的日历从不同的起始点计年,并使用不同的编号系统。伊斯兰历从公元 622 年开始计年,目前显示的年份大约是 1446 年。希伯来历从公元前 3761 年开始计年,目前显示的年份大约是 5785 年。中国农历使用一个 60 年的周期来命名年份,而不是连续的数字。

JavaScript 的 Intl.DateTimeFormat 通过 calendar 选项支持这些日历系统。可用的值包括 islamichebrewchinesepersian 等。year 选项控制是否显示年份,era 选项控制是否以及如何显示纪元标识。

使用命名年份的日历通过 formatToParts() 提供两部分信息。yearName 部分包含循环年份名称,relatedYear 部分包含对应的公历年份。这使得应用程序可以显示其中一个或两个值。

不同的语言环境以不同的方式格式化相同的日历。伊斯兰历在英文中以拉丁数字显示,在阿拉伯语中以阿拉伯-印度数字显示。格式会自动适应语言环境的惯例,同时保持日历的年份编号系统。