希吉来历日期 (乌姆·古拉历) 是做什么的?
沙特阿拉伯的官方希吉来历是乌姆·古拉历(أم القرى),一种计算型历法。其他“伊斯兰”历法使用不同规则,日期通常相差一天,因此在代码和用户界面中必须明确指定使用的历法。
规则
- 存储 ISO 公历日期(例如
2026-11-03,使用DATE列或 UTC 时间戳)。仅显示时转换为希吉来历。当用户输入希吉来历日期时,在输入时将其转换为公历并存储。 - 始终标明显示日期所使用的历法:例如 «23 جمادى الأولى 1448هـ» 和 «3 نوفمبر 2026م»。
- 希吉来历月份有 29 或 30 天,一年有 354 或 355 天。切勿使用固定月长或通过“增加 30 天”来计算“下个月”。
- 类似
1448-05-23的希吉来历字符串不是 ISO 8601。切勿将其传递给new Date(),否则会被解释为公元 1448 年。 - 宗教日期(斋月开始、开斋节)由观月宣布,可能与印刷的乌姆·古拉历日期不同。对于这些计算出的日期,应标注为 «حسب تقويم أم القرى»(根据乌姆·古拉历)或 «متوقع»(预计),切勿作为确定日期。
JavaScript:使用 Intl 格式化
const d = new Date(Date.UTC(2026, 10, 3)); // 2026年11月3日
new Intl.DateTimeFormat('ar-SA-u-ca-islamic-umalqura', { dateStyle: 'full', timeZone: 'UTC' }).format(d);
// "الثلاثاء، ٢٣ جمادى الأولى ١٤٤٨ هـ"
new Intl.DateTimeFormat('ar-SA-u-ca-islamic-umalqura-nu-latn',
{ day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC' }).format(d);
// "23 جمادى الأولى 1448 هـ"
new Intl.DateTimeFormat('en-u-ca-islamic-umalqura-nu-latn',
{ day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC' }).format(d);
// "Jumada I 23, 1448 AH"
- 始终显式指定
ca-islamic-umalqura。不要依赖ar-SA区域设置来自动使用希吉来历:在最新的 ICU/CLDR(Node 24,当前 Chrome)中,ar-SA的默认历法是公历。 - 使用
nu-latn(0-9)或nu-arab(٠-٩)显式设置数字格式。默认值因区域设置和版本而异:ar-SA默认显示 ٠-٩,而普通的ar现在显示 0-9。 - 精确的字符串(例如 «هـ» 之前的空格、“AH”、“Jumada I” vs “Jumada al-Ula”)在不同引擎中可能不同。切勿解析格式化后的文本,也切勿在不同环境中进行快照测试。应使用
formatToParts。 - 时区决定具体日期。
new Date('2026-11-03')是 UTC 午夜,因此应使用timeZone: 'UTC'格式化。对于“沙特阿拉伯今天”的日期,使用timeZone: 'Asia/Riyadh'。例如,时刻2026-11-03T22:30Z在利雅得已经是 24 جمادى الأولى。
公历转希吉来历数字
const hijriParts = new Intl.DateTimeFormat('en-u-ca-islamic-umalqura-nu-latn', {
day: 'numeric', month: 'numeric', year: 'numeric', timeZone: 'UTC',
});
export function toHijri(date) {
const p = {};
for (const { type, value } of hijriParts.formatToParts(date)) p[type] = value;
return { year: parseInt(p.year, 10), month: parseInt(p.month, 10), day: parseInt(p.day, 10) };
}
toHijri(new Date(Date.UTC(2026, 10, 3))); // { year: 1448, month: 5, day: 23 }
希吉来历转公历:扫描天数
Intl 无法解析希吉来历日期,因此需估计公历日期并扫描附近的日子,直到 toHijri 与之匹配。
const DAY = 86400000;
export function hijriToGregorian(y, m, d) {
// 1 Muharram 1 AH 约等于 622年7月19日(预测公历);月平均长度 29.530589 天
const est = Date.UTC(622, 6, 19) + Math.round((y - 1) * 354.36667 + (m - 1) * 29.530589 + (d - 1)) * DAY;
for (let off = -20; off <= 20; off++) {
const t = new Date(est + off * DAY);
const h = toHijri(t);
if (h.year === y && h.month === m && h.day === d) return t.toISOString().slice(0, 10);
}
return null; // 该日期不存在,例如某月有29天但用户输入了第30天
}
hijriToGregorian(1447, 9, 1); // "2026-02-18"
hijriToGregorian(1448, 1, 1); // "2026-06-16"
hijriToGregorian(1448, 1, 30); // null(Muharram 1448 只有29天)
- 返回
null可用于验证用户输入;此时应显示 «هذا اليوم غير موجود في هذا الشهر»(该日在此月份不存在)。 - 计算月份长度:
hijriToGregorian(y, m, 30) ? 30 : 29。 - ICU 的乌姆·古拉历表覆盖 1300 至 1600 AH 范围。超出该范围,引擎会回退到表格历法,因此返回的结果不是乌姆·古拉历。
- 接受用户输入的阿拉伯-印度数字:解析前将 ٠-٩ 转换为 0-9(参见
arabic-rtl-web)。
Python:hijridate
# pip install hijridate (hijri-converter 的后继者,API 相同)
from hijridate import Hijri, Gregorian
h = Gregorian(2026, 11, 3).to_hijri() # Hijri(1448, 5, 23)
h.isoformat() # '1448-05-23'
h.dmyformat() # '23/05/1448'
h.month_name() # 'Jumada al-Ula'
h.month_name('ar') # 'جمادى الأولى'
h.day_name('ar') # 'الثلاثاء'
h.notation('ar') # 'هـ'
h.month_length() # 30
g = Hijri(1447, 9, 1).to_gregorian() # Gregorian(2026, 2, 18),是 datetime.date 的子类
Hijri.today()
- 旧代码使用
pip install hijri-converter和from hijri_converter import Hijri, Gregorian。它仍然有效,但现在会发出 DeprecationWarning;请将导入改为hijridate。 - 支持范围:希吉来历 1 Muharram 1343 至 30 Dhu al-Hijjah 1500 AH(对应公历 1924-08-01 至 2077-11-16)。超出范围将引发
OverflowError: date out of range。 - 输入不存在的日期(如
Hijri(1448, 1, 30))将引发ValueError: day must be in 1..29 for month。 - 在 .NET 中,应使用
UmAlQuraCalendar,而不是HijriCalendar(后者是科威特表格历法算法)。
月份名称
1 محرم، 2 صفر, 3 ربيع الأول، 4 ربيع الآخر، 5 جمادى الأولى، 6 جمادى الآخرة، 7 رجب، 8 شعبان، 9 رمضان، 10 شوال، 11 ذو القعدة، 12 ذو الحجة。
不同库的拼写可能不同:ICU 打印 «ربيع الآخر» 而 hijridate 打印 «ربيع الثاني»;英文拼写也不同(如 “Rabiʻ II”、“Rabi' al-Thani”)。为保持 UI 一致,建议基于月份数字映射自己维护的列表,而不是混用不同库的字符串。
陷阱
- 错误的历法。 对于 2026 年 11 月 3 日,Node 24 使用
islamic-umalqura得到 23 日,使用islamic-civil得到 22 日,使用islamic得到 24 日。Excel 和 Windows 的“希吉来历”日历使用科威特表格算法。沙特日期应使用乌姆·古拉历。 - 时区导致的一天偏差:应在存储日期时所用的同一时区内进行格式化。
- 希吉来历中的日期运算。 要计算“下个月”,先增加月份数字,然后将日限制为该月的最大天数,最后进行转换。年龄和持续时间应在公历中计算。
- 未标注的日期。 裸的 «1/5/1448» 或 «5/1» 有歧义;始终添加 هـ 或 م 进行区分。
- 数字系统混用。 每页应保持使用统一的数字系统。
使用已知对照表进行测试(乌姆·古拉历)
| 希吉来历 | 公历 |
|---|---|
| 1 Ramadan 1447 | 2026-02-18 |
| 1 Muharram 1448 | 2026-06-16 |
| 23 Jumada al-Ula 1448 | 2026-11-03 |
| 10 Dhu al-Hijjah 1446 | 2025-06-06 |