Communitygithub.com

希吉来历日期 (乌姆·古拉历)

处理代码中的伊斯兰希吉来历日期,使用沙特阿拉伯的乌姆·古拉历法,包括 JavaScript Intl 格式化、公历与希吉来历互转、Python 库转换、校验、存储和标注。适用于需要希吉来历(التاريخ الهجري، تقويم أم القرى)、伊斯兰月份名称、日期转换或沙特日期显示的应用、表单、报告或数据库。

希吉来历日期 (乌姆·古拉历) 是什么?

该技能提供了在代码中处理希吉来历(伊斯兰历)日期的完整解决方案,特别针对沙特阿拉伯官方采用的乌姆·古拉历(Umm al-Qura)。它涵盖了 JavaScript 和 Python 两大平台:在 JavaScript 中,利用 `Intl.DateTimeFormat` 进行格式化,并通过“扫描天”算法实现希吉来历与公历之间的精确转换;在 Python 中,推荐使用 `hijridate` 库(前身为 `hijri-converter`)进行转换和验证。技能详细说明了存储规则(始终以 ISO 公历日期存储,仅显示时转换)、月份名称(阿拉伯语和英语)、常见陷阱(如时区偏差、错误历法导致一天差异),并提供了可验证的对照表。对于开发者而言,此技能不仅提供了现成的代码片段和最佳实践,还强调了在用户界面中明确标注历法(如“23 جمادى الأولى 1448هـ”)和正确处理宗教日期(如斋月开始)的重要性,确保在沙特或伊斯兰背景下应用的准确性和用户体验。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/hosam382/arabic-claude-skills/tree/HEAD/hijri-dates

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

希吉来历日期 (乌姆·古拉历) 是做什么的?

沙特阿拉伯的官方希吉来历是乌姆·古拉历(أم القرى),一种计算型历法。其他“伊斯兰”历法使用不同规则,日期通常相差一天,因此在代码和用户界面中必须明确指定使用的历法。

规则

  1. 存储 ISO 公历日期(例如 2026-11-03,使用 DATE 列或 UTC 时间戳)。仅显示时转换为希吉来历。当用户输入希吉来历日期时,在输入时将其转换为公历并存储。
  2. 始终标明显示日期所使用的历法:例如 «23 جمادى الأولى 1448هـ» 和 «3 نوفمبر 2026م»。
  3. 希吉来历月份有 29 或 30 天,一年有 354 或 355 天。切勿使用固定月长或通过“增加 30 天”来计算“下个月”。
  4. 类似 1448-05-23 的希吉来历字符串不是 ISO 8601。切勿将其传递给 new Date(),否则会被解释为公元 1448 年。
  5. 宗教日期(斋月开始、开斋节)由观月宣布,可能与印刷的乌姆·古拉历日期不同。对于这些计算出的日期,应标注为 «حسب تقويم أم القرى»(根据乌姆·古拉历)或 «متوقع»(预计),切勿作为确定日期。

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 14472026-02-18
1 Muharram 14482026-06-16
23 Jumada al-Ula 14482026-11-03
10 Dhu al-Hijjah 14462025-06-06

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相关技能