Gutenberg wordpress/components DateTimePicker日期时间选择器的组件结构与时区处理机制【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg本篇指南基于 DateTimePicker 官方文档系统讲解 WordPress Gutenberg 仓库中wordpress/components包提供的DateTimePicker组件从 Props 完整参数、DatePicker与TimePicker两个子组件的拆分使用到源码层面的时区归一化、键盘导航与无障碍标签实现。读完你可以掌握如何在编辑器插件、小工具或仪表盘中安全地处理日期时间输入并理解其输出字符串格式与时区配置之间的对应关系。组件总览与导出结构DateTimePicker是一个渲染日历calendar与时钟输入clock/time input的 React 组件用于日期和时间的联合选择。日历和时钟也可以拆开分别通过DatePicker和TimePicker两个组件单独使用。从 入口文件 可以看到三者统一的导出关系import { default as DatePicker } from ./date-picker; import { default as TimePicker } from ./time-picker; import { default as DateTimePicker } from ./date-time; export { DatePicker, TimePicker }; export default DateTimePicker;即DateTimePicker是该模块的默认导出DatePicker和TimePicker是具名导出二者也可以作为独立控件用于只需要日期或只需要时间字段的表单。DateTimePicker 的组装方式查看 DateTimePicker 实现它本身是一个轻量的组合层Wrapper ref{ ref } classNamecomponents-datetime spacing{ 4 } TimePicker currentTime{ currentDate } onChange{ onChange } is12Hour{ is12Hour } dateOrder{ dateOrder } / DatePicker currentDate{ currentDate } onChange{ onChange } isInvalidDate{ isInvalidDate } events{ events } onMonthPreviewed{ onMonthPreviewed } startOfWeek{ startOfWeek } / /Wrapper可以看出几个事实时间输入区TimePicker与日历区DatePicker共享同一个currentDate和同一个onChange回调因此任一区域的变化都会以统一格式向上传递is12Hour和dateOrder只传给TimePicker它们只影响日期字段的排列与 12/24 小时制而isInvalidDate、events、onMonthPreviewed、startOfWeek只作用于日历区onMonthPreviewed的缺省值是空函数noop不传也不会有副作用组件通过forwardRef暴露根节点类名为components-datetime子节点分别为components-datetime__time与components-datetime__date这些类名在源码中被标注为“Unused, for backwards compatibility”即旧版样式选择器仍可继续使用。基本用法文档给出的标准用法是受控模式下用useState持有日期把currentDate传给组件并在onChange中回写状态。is12Hour传入true时显示 AM/PM 切换控件且日期字段顺序按mdy处理import { useState } from react; import { DateTimePicker } from wordpress/components; const MyDateTimePicker () { const [ date, setDate ] useState( new Date() ); return ( DateTimePicker currentDate{ date } onChange{ ( newDate ) setDate( newDate ) } is12Hour{ true } / ); };如果只需要日期或只需要时间可以分别使用拆分组件例如import { DatePicker, TimePicker } from wordpress/components; const MyDatePicker () { const [ date, setDate ] useState( new Date() ); return ( DatePicker currentDate{ date } onChange{ ( newDate ) setDate( newDate ) } / ); };以上拆分用法与 DatePicker 源码中的官方示例 和 TimePicker 源码中的官方示例 保持一致。Props 完整参数说明types.ts 中定义了完整的 TypeScript 类型DateTimePickerProps是DatePickerProps与TimePickerProps的合并剔除onChange/currentTime/hideLabelFromVision后用统一的onChange替代。各参数说明如下currentDate:Date | string | number | null初始化时的当前日期时间。可以传入null表示当前未选择任何日期。必填否默认今天的日期源码中为currentDate ?? new Date()见 DatePicker 实现onChange:( date: string | null ) void用户选择了新的日期或时间时调用的函数。参数是新的日期时间字符串——源码中统一以TIMEZONELESS_FORMAT格式化后传出见下文时区处理一节即形如2026-09-16T11:52:44的不含时区信息的 ISO 8601 字符串。必填否is12Hour:boolean是否使用 12 小时制。为true时显示 AM/PM 切换控件并且日期字段顺序假设为mdy即 Month-Day-Year而默认的 24 小时制顺序是dmyDay-Month-Year。类型boolean必填否默认false在 TimePicker 实现 中可以看到这个默认顺序的落地逻辑const defaultDateOrder is12Hour ? mdy : dmy;dateOrder:dmy | mdy | ymd日、月、年的排列顺序会覆盖由is12Hour推断出的字段顺序。源码中只有[dmy, mdy, ymd]三个值合法传入其他值会回退到默认顺序const VALID_DATE_ORDERS [ dmy, mdy, ymd ]; // ... const dateOrder dateOrderProp VALID_DATE_ORDERS.includes( dateOrderProp ) ? dateOrderProp : defaultDateOrder;类型string必填否默认dmyisInvalidDate:( date: Date ) boolean回调函数接收代表某一天的Date对象返回布尔值表示该天是否合法。返回true的日期会被渲染为禁用按钮disabled用户无法在日历中选中也不能通过键盘导航选中。在 DatePicker 实现 中每个日期按钮的isInvalid属性正是由该回调计算得出的。必填否onMonthPreviewed:( date: string ) void用户在日期选择器中切换到上一个月/下一个月时触发。回调接收新月份的日期格式为不含时区的 ISO 字符串。从源码看它的触发点有三处点击“查看上一个月”按钮prev 按钮 onClick点击“查看下一个月”按钮next 按钮 onClick键盘导航如 PageUp/PageDown 或上下方向键导致焦点跨月时onKeyDown 处理。这使得父组件可以在不改变已选中日期的前提下预取“即将被浏览月份”的数据例如某月的日程或活动列表。必填否events:{ date: Date }[]要在日期选择器中展示的事件列表每个事件在其对应日期上显示为一个圆点。源码中通过events.filter( event isSameDay( event.date, day ) ).length统计每天的numEvents圆点仅在numEvents 0时显示见 DatePicker 渲染逻辑 与DayButton的hasEvents属性。事件数量还会并入屏幕阅读器标签“There is %d event” / “There are %d events”。类型Array必填否startOfWeek:number每周的起始日。0 为周日1 为周一依此类推。必填否默认0周日该值在类型层面被收窄为字面量联合0 | 1 | 2 | 3 | 4 | 5 | 6见 DatePickerProps 定义并传入useLilius日历生成 Hook 的weekStartsOn参数同时参与Home/End键跳转到“本周首/末日”的计算( dayOfWeek - weekStartsOn 7 ) % 7。DatePicker日历子组件的实现细节DatePicker位于 date-picker/index.tsx源码中有几个值得注意的设计日历网格生成网格数据来自 use-lilius Hook传入当前选中日期、当前浏览月份和weekStartsOn返回calendar按周分组的二维日期数组、viewing当前浏览的月、setSelected/setViewing等控制方法。渲染时只绘制与viewing同月的日期isSameMonth( day, viewing )为 false 的格子直接不渲染保证日历始终是整齐的网格。键盘导航roving tabindex日历支持完整的方向键操作见 onKeyDown 处理按键焦点移动ArrowLeft/ArrowRight前/后一天RTL 语言下方向自动反转ArrowUp/ArrowDown前/后一周PageUp/PageDown前/后一个月Home/End本周边startOfWeek的第一/最后一天焦点管理采用 roving tab index 模式只有focusable指向的那一天拥有tabIndex{ 0 }其余日期按钮均为tabIndex{ -1 }每次按键后先setFocusable若目标日期落在浏览月份之外还会同步setViewing并触发onMonthPreviewed。另外源码特意用isFocusWithinCalendar状态区分“焦点是否本来就在日历内”避免程序化focus()抢走 TimePicker 输入框的焦点。无障碍标签日历容器带有roleapplication与aria-labelCalendar每个日期按钮的aria-label由 getDayLabel 拼装包含本地化日期、Selected已选中、Today今天以及事件数量描述屏幕阅读器可以获得完整的上下文。月份标题通过dateI18n( F )/dateI18n( Y )本地化输出前后月按钮的箭头图标在 RTL 环境下自动互换方向。“今天”高亮与智能默认值文档的 Best Practices 一节要求日期选择器“使用智能默认值并高亮当前日期”。在源码中这体现为两点未传currentDate时默认取new Date()今天每天计算isToday isSameDay( day, startOfDayInConfiguredTimezone( new Date() ) )并传给DayButton的isToday属性用于视觉高亮。TimePicker时间输入子组件的实现细节TimePicker位于 time-picker/index.tsx它并不是一个模拟钟面而是一组表单输入控件包含两个fieldsetTime 字段组内部TimeInput控件小时 分钟输入12 小时制下附加 AM/PM 切换加一个TimeZone展示组件Date 字段组按dateOrder排列的 Day数字输入、MonthSelectControl下拉12 个本地化月份名、Year数字输入。几个源码层面的约束值得注意Day 输入的min为 1max由getDaysInMonth( year, month - 1 )动态计算利用new Date( year, month 1, 0 ).getDate()的日期回绕逻辑见 utils.ts因此输入不会超出当月实际天数Year 输入min1、max9999并通过buildPadInputStateReducer( 4 )保证值始终零填充为 4 位小时/分钟同理填充为 2 位例如4显示为04每次字段变更都会先经validateInputElementTarget校验确认目标是合法的input元素再通过setInConfiguredTimezone更新内部日期最后调用onChange( formatDate( TIMEZONELESS_FORMAT, newDate ) )时间值内部以 24 小时制存储TimeInputValue.hours12 小时制显示通过 from24hTo12h / from12hTo24h 转换TimePicker.TimeInput作为静态属性单独导出可以脱离完整的 TimePicker 单独使用值为{ hours, minutes }对象24 小时制hideLabelFromVisionTimePicker单独使用时可用为true时Time/Date的legend标签改用VisuallyHidden只保留给屏幕阅读器。TimePicker还支持currentTime初始值且初始化时用startOfMinute截断到分钟——源码注释指向历史 issue #15495避免秒数干扰展示。时区归一化与输出格式这是理解该组件行为的关键。constants.ts 定义了统一的输出格式export const TIMEZONELESS_FORMAT Y-m-d\\TH:i:s;所有onChange回调传出的字符串都按此格式生成即YYYY-MM-DDTHH:mm:ss例如2026-09-16T11:52:44。注意它是“不含时区”timezoneless的——它表示的是 WordPress 站点配置时区下的墙钟时间而不是某个 UTC 时刻。utils.ts 中的inputToDate负责把三种输入统一为内部表示带时区标识Z或08:00等的字符串按 ISO 解析后归一化为 UTC 时刻不含时区的字符串正是onChange输出的那种格式按wordpress/date配置的时区偏移解析后再转为 UTC 存储Date对象或毫秒时间戳直接表示确定的 UTC 时刻。配套的setInConfiguredTimezone与startOfDayInConfiguredTimezone解决了一个经典坑date-fns 的日期运算默认基于浏览器本地时区当浏览器时区与站点配置时区不一致时会产生“差一天”的 bug。源码注释给出了具体例子——UTC 时间 11 月 16 日 01:00配置时区为 UTC-5 时站点日历日其实是 11 月 15 日。因此日历内部用startOfDayInConfiguredTimezone计算“配置时区下的当日零点”用setInConfiguredTimezone在“按配置时区读字段 → 拼回 timezoneless 字符串 → 重新解析为 UTC 时刻”的往返中更新字段并对date做当月天数钳制避免产生2026-02-31这类非法字符串见 utils.ts#L144-L147。实践含义把onChange得到的字符串直接存入 WordPress 数据库即为“站点时区的墙钟时间”若前端需要再次传回组件直接复用该字符串即可inputToDate会按同一配置时区正确解析。跨时区场景下请始终依赖wordpress/date的配置时区而不是浏览器本地时区。参考文件索引文件说明packages/components/src/date-time/README.md组件文档本文的事实来源packages/components/src/date-time/index.ts模块导出入口packages/components/src/date-time/types.tsDatePickerProps/TimePickerProps/DateTimePickerProps类型定义packages/components/src/date-time/date-time/index.tsxDateTimePicker组合层实现packages/components/src/date-time/date-picker/index.tsx日历渲染、键盘导航与无障碍标签packages/components/src/date-time/date-picker/use-lilius/日历网格生成 Hookpackages/components/src/date-time/time-picker/index.tsx时间/日期输入字段实现packages/components/src/date-time/utils.ts时区归一化、12/24 小时制转换等工具函数packages/components/src/date-time/constants.tsTIMEZONELESS_FORMAT输出格式常量【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
