☰
react-day-picker 希伯来日历扩展 @daypicker/hebrew:安装、用法与闰年实现原理
2026/10/9 5:06:44 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】react-day-picker

DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications.

项目地址:https://gitcode.com/gh_mirrors/re/react-day-picker
点击查看免费下载

本指南以 React DayPicker 的希伯来历法扩展包@daypicker/hebrew(仓库内位于 packages/hebrew)为核心,讲解如何在 React 应用中安装并渲染带希伯来农历(lunisolar)月历逻辑的日期选择器,包括闰年中 Adar I / Adar II 的月份逻辑,以及包默认启用的希伯来语与从右到左(RTL)布局。读完本文,你将掌握@daypicker/hebrew的完整安装步骤、两种开箱即用的渲染方案(希伯来语默认配置与英语 + LTR + 拉丁数字配置),并能结合源码理解其希伯来历法日期转换、月份序列与格式化的底层实现。

包定位:希伯来历法专用构建

@daypicker/hebrew是 DayPicker 的专用日历构建之一,与仓库中同样位于 packages 下的buddhist、ethiopic、hijri、persian等日历包同属一类:它们不改变 DayPicker 的组件交互模型(单选、多选、范围选择等),而是替换底层的时间/日期计算库(DateLib)与区域设置,让网格渲染、月份导航、格式化全部遵循目标历法。

包的描述与职责在 package.json 中写得很清楚:"Hebrew calendar support for react-day-picker"。其依赖关系也印证了这一点——@daypicker/hebrew直接依赖@daypicker/react与date-fns(package.json),因此使用前需要先安装基础包。

包的入口文件 packages/hebrew/src/index.tsx 重新导出了希伯来历法实现(export * from "./hebrew/index.js"),而 packages/hebrew/src/classes/DateLib.ts 则直接复用并重导出@daypicker/react的DateLib与DateLibOptions类型,说明希伯来包是在标准 DayPicker 之上注入历法逻辑,而不是另起炉灶。

安装

与 README 中给出的命令一致,需要同时安装 React DayPicker 基础包与希伯来日历包:

npm install @daypicker/react @daypicker/hebrew
  • @daypicker/react提供DayPicker组件本体、样式文件与类型;
  • @daypicker/hebrew提供希伯来历法实现与封装后的DayPicker入口。

以当前仓库为例,两个包的版本保持一致(@daypicker/hebrew为10.0.1,其依赖@daypicker/react同样为10.0.1,见 package.json),便于按版本配套升级。仓库使用 pnpm workspace 管理,若在本地以源码方式调试,可参考 pnpm-workspace.yaml 中包结构的组织方式。

基础用法

安装完成后,从@daypicker/hebrew导入DayPicker,并引入基础包的样式:

import { DayPicker } from "@daypicker/hebrew"; import "@daypicker/react/style.css"; export function HebrewCalendar() { return <DayPicker mode="single" />; }

这段代码来自 packages/hebrew/README.md。注意两点:

  1. 样式来自基础包:@daypicker/hebrew不携带独立样式文件,网格布局、按钮样式等均复用@daypicker/react/style.css;
  2. mode等选择模式 props 照常可用:希伯来包只是替换历法,single、multiple、range等选择模式与 DayPicker 完全一致。

包内DayPicker组件的默认行为(源码注释见 packages/hebrew/src/hebrew/index.tsx)可总结为:

默认项值说明
localehe希伯来语区域(月份、星期标签为希伯来文)
dirrtl从右到左布局,符合希伯来文书写方向
numeralslatn使用拉丁数字(而非希伯来传统希伯来数字符号)

对应地,packages/hebrew/src/hebrew/index.tsx 在渲染底层组件时依次注入locale={locale}、numerals={props.numerals ?? "latn"}、dir={props.dir ?? "rtl"},未显式传入时即采用上述默认值。周结构保持周日至周六(Weeks remain Sunday–Saturday),这与 DayPicker 通用的weekStartsOn配置不同,属于希伯来历法包固定的周排列。

仓库内的最小可运行示例是 examples/Hebrew.tsx,它仅以一个无 props 的<DayPicker />渲染完整希伯来日历;对应快照测试(packages/hebrew/src/hebrew/index.test.tsx)验证了默认渲染下网格标题为希伯来文月份名תשרי 5785(Tishrei 5785 年)。

英语标签、LTR 布局与拉丁数字

希伯来包同时导出了enUS区域设置,用于希望保留希伯来历法逻辑、但以英语显示标签的场景。README 虽未给出该示例,但官方文档与仓库示例均有完整实现(apps/website/docs/localization/hebrew.mdx、examples/HebrewEn.tsx):

import { DayPicker, enUS } from "@daypicker/hebrew"; export function HebrewCalendarEn() { return <DayPicker locale={enUS} dir="ltr" numerals="latn" />; }

三个 props 的取值含义:

  • locale={enUS}:月份与星期标签改为英文(月份仍按希伯来历法,如 "Tishri 5785");
  • dir="ltr":切换为从左到右布局;
  • numerals="latn":数字字形保持拉丁数字。

enUS与he均由包导出,其实际定义来自@daypicker/react/locale(见 packages/hebrew/src/locale/en-US.ts 与 packages/hebrew/src/locale/he.ts)。测试用例 packages/hebrew/src/hebrew/index.test.tsx 验证了locale={enUS} dir="ltr" numerals="latn"时网格显示英文月份 "Tishri 5785",且网格中的星期四单元格(Thursday)正常出现——说明星期序列并未因切换语言而改变。

闰年与月份序列:Adar I / Adar II 的实现

希伯来历法是阴历-阳历混合(lunisolar)历法:月份跟随月相,同时通过 19 年置闰周期使年份与太阳年对齐。这是@daypicker/hebrew与公历 DayPicker 最大的差异所在,也是其核心价值。

平年与闰年的月份表

源码在 packages/hebrew/src/hebrew/utils/constants.ts 中定义了两套月份序列:

// 平年(12 个月) const MONTH_SEQUENCE_COMMON = [ "tishrei", "cheshvan", "kislev", "tevet", "shevat", "adar", // ← 普通 Adar "nisan", "iyar", "sivan", "tamuz", "av", "elul", ] as const; // 闰年(13 个月,插入 Adar I) const MONTH_SEQUENCE_LEAP = [ "tishrei", "cheshvan", "kislev", "tevet", "shevat", "adarI", // ← 闰年插入的 Adar I "adar", // ← 原 Adar 变为 Adar II "nisan", "iyar", "sivan", "tamuz", "av", "elul", ] as const;

在闰年中,原本的adar月份之前插入adarI(Adar I),原来的adar随之承担 Adar II 的角色。这就是 README 中"闰年包含 Adar I 与 Adar II"说法的直接来源。19 年周期内含 235 个朔望月(MONTHS_PER_CYCLE = 235,见 constants.ts),平均每年 12.37 个月,正对应 19 年 7 个闰年的置闰规则。

闰年判定与罗什·哈沙纳计算

判定某一年是否为希伯来闰年,源码使用经典的 19 年周期取模规则(packages/hebrew/src/hebrew/utils/calendarMath.ts):

/** Determine whether a Hebrew year includes the extra Adar I month. */ export function isHebrewLeapYear(year: number): boolean { return mod(7 * year + 1, 19) < 7; }

mod为总是返回正余数的取模实现(calendarMath.ts),用于正确处理 19 年循环。围绕它,同一文件中实现了完整的希伯来年历推算:

  • monthsElapsed(year):计算某年之前累计经过的阴历月数(Math.floor((235 * year - 234) / 19));
  • hebrewCalendarElapsedDays(year):按希伯来历法传统(每月的"部分小时数"parts 累加、罗什·哈沙纳推迟规则)推算新年(Rosh Hashanah)所在绝对日;
  • roshHashanah(year):返回新年绝对日,并使用Map缓存结果(calendarMath.ts),避免重复计算;
  • daysInHebrewYear(year):由两个相邻新年的绝对日之差得出整年天数,同样带缓存(calendarMath.ts)。

这些推算支持了daysInHebrewMonth、monthsInHebrewYear等后续函数,是整个希伯来历法包能够正确构建月份网格的数学地基。判断年份长短(缺年 deficient / 常年 regular / 完年 complete)的逻辑也在该文件中继续展开,这与希伯来历法中 353/354/355 天(闰年 383/384/385 天)的年型分类相对应。

月份序列化与日期换算

历法内部通过"从纪元(epoch,提斯利月第 1 年)起的序列月份索引"统一处理月份增减与导航(packages/hebrew/src/hebrew/utils/serial.ts):

  • monthsBeforeYear(year):以 19 年循环(MONTHS_PER_CYCLE)为单元,累加每个希伯来年的实际月数(平年 12、闰年 13),得到某年前累计的月份数;
  • monthsSinceEpoch({ year, monthIndex }):由此得到任意希伯来日期在序列上的全局月份索引;
  • 反向的hebrewFromMonthIndex(monthIndex)支持负数索引,即纪元之前的日期也能换算回希伯来年/月(serial.ts)。

月份导航的入口函数addMonths(date, amount)(packages/hebrew/src/hebrew/lib/addMonths.ts)正是基于这套序列:先把公历Date换算为希伯来日期(toHebrewDate),在序列索引上加减月份数,再反算目标希伯来年月,最后用clampHebrewDay把"日"钳制到目标月的合法范围(避免如从 30 日的月份跳到只有 29 日的月份时越界),再换算回公历Date。lib目录下还有addYears、setMonth、setYear、eachMonthOfInterval、startOfMonth、endOfYear等一整套对应实现(见 packages/hebrew/src/hebrew/lib),且每个函数都有配套测试(如 addMonths.test.ts),保证闰年边界下的行为正确。

月份与星期的格式化实现

希伯来包对日期格式化做了完整覆盖,核心在 packages/hebrew/src/hebrew/lib/format.ts 中的format函数。它优先使用Intl.DateTimeFormat配合calendar: "hebrew"生成符合目标语言的字符串,同时提供内置回退表,防止运行环境(如某些 Node 版本)对希伯来历法 ICU 支持不完整时崩溃。

  • 月份名:formatMonthName使用Intl.DateTimeFormat(localeCode, { month: "long", calendar: "hebrew" });失败时回退到硬编码的 13 个月份名称表(format.ts),其中包含adarI的希伯来文 "אדר א׳" 与英文 "Adar I";
  • 星期名:formatWeekdayName同理,回退表提供了长格式("יום ראשון"…"שבת")与窄格式("א","ב",…"ש")两套希伯来文星期名(format.ts);
  • 模板支持:LLLL y/LLLL yyyy(月 + 年标题)、PPP/PPPP(长/完整日期)、cccc/cccccc(星期)、yyyy-MM-dd(ISO 风格,月号用hebrewMonthNumber计算,使闰年中的 Adar I 有独立序号)以及HH:mm之类的时间模板,其余未匹配模板回退为日/月/年格式(format.ts)。

这套格式化实现与 DayPicker 的formatCaption、formatWeekdayName等钩子配合,保证月标题、星期表头与无障碍标签在希伯来历法下输出正确文本。

覆盖与自定义:dateLib 扩展点

希伯来包的DayPicker允许通过dateLibprop 覆盖底层日期库方法。这在 packages/hebrew/src/hebrew/index.tsx 的getDateLib中实现:

export const getDateLib = (options?: DateLibOptions & { overrides?: DayPickerProps["dateLib"] }) => { const { overrides, ...dateLibOptions } = options ?? {}; return new DateLib(dateLibOptions, { ...hebrewDateLib, // 希伯来历法默认实现(addMonths、format 等) ...(overrides ?? {}), // 用户覆盖,优先级最高 }); };

即:希伯来历法实现作为默认,用户的overrides按同名 key 覆盖之。测试 packages/hebrew/src/hebrew/index.test.tsx 演示了这一用法——传入dateLib={{ format: () => "custom caption" }}后,网格标题即变为自定义文本,证明覆盖生效且优先级高于希伯来默认格式化。

这一点在实测中很有用:当你想保留希伯来月份/闰年逻辑、但自定义标题格式(如加上年份说明)时,无需 fork 整个包,只需注入一个format覆盖函数。

在仓库中进一步探索

若想深入验证或二次开发,仓库提供了完整的可运行示例与测试:

  • 最小示例:examples/Hebrew.tsx 与英文标签示例 examples/HebrewEn.tsx;
  • 单元测试:packages/hebrew/src/hebrew/index.test.tsx 覆盖默认渲染、区域切换与dateLib覆盖三类场景;lib与utils目录内每个历法函数均配套测试(如 addMonths.test.ts、calendarMath.test.ts);
  • 官方文档:apps/website/docs/localization/hebrew.mdx 提供了与本文对应的浏览器内实时示例(含英文标签变体);
  • 历法核心源码:packages/hebrew/src/hebrew/utils/constants.ts(月份序列与纪元常量)、calendarMath.ts(罗什·哈沙纳与闰年推算)、dateConversion.ts(希伯来—公历互转)、format.ts(格式化)。

小结

@daypicker/hebrew以"基础包 + 历法包"的组合方式,让 React DayPicker 支持希伯来农历:安装@daypicker/react与@daypicker/hebrew后,从希伯来包导入DayPicker即可获得希伯来语、RTL 布局的月历,闰年自动出现 Adar I / Adar II;通过locale、dir、numerals三个 props 可一键切换到英文标签与 LTR 布局;底层则由 19 年置闰周期、罗什·哈沙纳推迟规则、序列化月份索引与Intl优先的格式化机制共同支撑。对于需要希伯来历法日期选择(如宗教日历、以色列本地化产品)的 React 应用,这是一个开箱即用、且留有dateLib自定义扩展点的成熟方案。

  • UI组件
  • 前端

【免费下载链接】react-day-picker

DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications.

项目地址:https://gitcode.com/gh_mirrors/re/react-day-picker
点击查看免费下载
上一篇:Phoenix Evals 示例运行指南:用 tsx 快速跑通 TypeScript 评估器示例
下一篇:openeuler/k8s-install发布工具详解:如何一键构建在线/离线安装包

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询