title: 日期与时间 description: 如何在 Reka UI 中处理日期和时间。
日期与时间
我们日期相关组件的内部机制深受 Adobe React Aria 团队的研究和工作的启发。该团队创建了在可访问性、用户体验和灵活性方面表现出色的强大日期组件。
该组件依赖于 @internationalized/date 包,它解决了在 JavaScript 中处理日期和时间时遇到的许多问题。
我们强烈建议阅读该包的文档以深入了解其工作原理,并且您需要在项目中安装它才能使用日期相关的组件。
$ npm add @internationalized/date日期对象
我们使用 @internationalized/date 提供的 DateValue 对象来表示各种组件中的日期。这些对象是不可变的,并提供有关它们所表示的日期类型的信息:
CalendarDate:没有时间组件的日期,例如2023-10-11。CalendarDateTime:有时间组件但没有时区的日期,例如2023-10-11T12:30:00。ZonedDateTime:有时间组件和时区的日期,例如2023-10-11T21:00:00:00-04:00[America/New_York]。
使用这些对象的好处是,我们可以非常精确地指定想要的日期类型,构建器的行为将适应该类型。
此外,您不必担心处理时区、夏令时或任何其他与日期相关的细节。
工具函数
该包还提供了许多工具函数,解决了在 JavaScript 中处理日期和时间时遇到的许多问题。
专门设计用于与 @internationalized/date 良好配合。
DateValue 类型
CalendarDate
表示没有任何时间组件的日期。这适用于只有日期重要的情况,如生日、周年纪念日或截止日期。
// 创建 CalendarDate
import { CalendarDate, getLocalTimeZone, parseDate, today } from '@internationalized/date'
// 从年、月、日参数创建
const date = new CalendarDate(2024, 7, 10)
// 从 ISO 8601 字符串创建
const parsedDate = parseDate('2024-07-10')
// 特定时区的当前日期
const losAngelesToday = today('America/Los_Angeles')
// 用户时区的当前日期
const localToday = today(getLocalTimeZone())查看 CalendarDate API 文档 了解更多方法。
CalendarDateTime
表示有时间组件但没有时区信息的日期。这对于有特定时间但不与特定时区绑定的事件很有用,如本地约会。
// 创建 CalendarDateTime
import { CalendarDateTime, parseDateTime } from '@internationalized/date'
// 从日期和时间组件创建
const dateTime = new CalendarDateTime(2024, 7, 10, 12, 30, 0)
// 从 ISO 8601 字符串创建
const parsedDateTime = parseDateTime('2024-07-10T12:30:00')查看 CalendarDateTime API 文档 了解更多方法。
ZonedDateTime
表示特定时区中的特定日期和时间。这对于在确切时刻发生的事件至关重要,无论用户位置如何,如会议、实时广播或国际会议。
// 创建 ZonedDateTime
import {
parseAbsolute,
parseAbsoluteToLocal,
parseZonedDateTime,
ZonedDateTime,
} from '@internationalized/date'
const date = new ZonedDateTime(
2024, // 年
7, // 月
10, // 日
'America/Los_Angeles', // 时区
-25200000, // UTC 偏移量(毫秒)(PDT)
12, // 小时
30, // 分钟
0 // 秒
)
// 使用不同的解析函数从 ISO 8601 字符串创建
const date1 = parseZonedDateTime('2024-07-12T00:45[America/New_York]')
const date2 = parseAbsolute('2024-07-12T07:45:00Z', 'America/New_York')
const date3 = parseAbsoluteToLocal('2024-07-12T07:45:00Z')查看 ZonedDateTime API 文档了解更多信息。
更新 DateValue 对象
由于 DateValue 对象是不可变的,因此在更新它们时必须创建新实例。以下是修改它们的正确方法:
// 错误 - 不会工作
let placeholder = new CalendarDate(2024, 7, 10)
placeholder.month = 8 // 错误!DateValue 对象是不可变的
// 正确 - 使用返回新实例的方法
let placeholder = new CalendarDate(2024, 7, 10)
// 方法 1:使用 set() 更改特定属性
placeholder = placeholder.set({ month: 8 })
// 方法 2:使用 add() 增加值
placeholder = placeholder.add({ months: 1 })
// 方法 3:使用 subtract() 减少值
placeholder = placeholder.subtract({ days: 5 })
// 方法 4:使用 cycle() 循环遍历有效值
placeholder = placeholder.cycle('month', 'forward', [1, 3, 5, 7, 9, 11])解析
解析日期字符串
当处理来自 API 或数据库的日期字符串时,根据需要的 DateValue 类型使用适当的解析函数:
import {
parseAbsolute, // 用于从 UTC 字符串 + 时区创建 ZonedDateTime
parseAbsoluteToLocal, // 用于在本地时区创建 ZonedDateTime
parseDate, // 用于 CalendarDate
parseDateTime, // 用于 CalendarDateTime
parseZonedDateTime, // 用于带时区名称的 ZonedDateTime
} from '@internationalized/date'
// 示例
const date = parseDate('2024-07-10') // CalendarDate
const dateTime = parseDateTime('2024-07-10T12:30:00') // CalendarDateTime
const zonedDate = parseZonedDateTime('2024-07-12T00:45[America/New_York]') // ZonedDateTime
const absoluteDate = parseAbsolute('2024-07-12T07:45:00Z', 'America/New_York') // ZonedDateTime
const localDate = parseAbsoluteToLocal('2024-07-12T07:45:00Z') // 用户时区的 ZonedDateTime常见陷阱和技巧
- 月份索引:与 JavaScript 的 Date 对象(0 索引)不同,@internationalized/date 使用 1 索引的月份(一月 = 1)。
- 不可变性:修改日期对象时总是重新赋值:
date = date.add({ days: 1 })。 - 时区处理:对于日程关键的事件(如会议或约会)使用
ZonedDateTime。 - 类型一致性:根据需求匹配您的 DateValue 类型 - 如果需要时间选择,使用
CalendarDateTime而不是CalendarDate。 - 解析函数:选择正确的解析函数以避免意外结果。例如,对仅日期字符串使用
parseDate,对不带时区的日期时间字符串使用parseDateTime。
如何使用?
import type { DateValue } from '@internationalized/date'
import { CalendarDate } from '@internationalized/date'
import {
createDateRange,
createDecade,
createMonth,
createYear,
createYearRange,
getDaysInMonth,
getWeekNumber,
hasTime,
isAfter,
isAfterOrSame,
isBefore,
isBeforeOrSame,
isBetween,
isBetweenInclusive,
isCalendarDateTime,
isZonedDateTime,
parseStringToDateValue,
toDate,
} from 'reka-ui/date'
const date = new CalendarDate(1995, 8, 18)
const minDate = new CalendarDate(1995, 8, 1)
const maxDate = new CalendarDate(1995, 8, 31)
parseStringToDateValue('1995-08-18', date) // 返回一个 DateValue 对象
toDate(date) // 返回一个 Date 对象
isCalendarDateTime(date) // 返回 false
isZonedDateTime(date) // 返回 false
hasTime(date) // 返回 false
getDaysInMonth(date) // 返回 31
getWeekNumber(new CalendarDate(1995, 8, 18), 'en-US', 'sun') // 返回 33
isAfter(date, minDate) // 返回 true
isBeforeOrSame(date, maxDate) // 返回 true
isAfterOrSame(date, minDate) // 返回 true
isBefore(date, maxDate) // 返回 true
isBetweenInclusive(date, minDate, maxDate) // 返回 true
isBetween(date, minDate, maxDate) // 返回 true
createMonth({ dateObj: new CalendarDate(1995, 8, 18), weekStartsOn: 0, locale: 'en', fixedWeeks: true }) // 返回该月的日期网格作为 DateValue,还包含 dateObj,以及该月的日期数组
createYear({ dateObj: new CalendarDate(1995, 8, 18), numberOfMonths: 2, pagedNavigation: true }) // 返回月份数组作为 DateValue,围绕 dateObj 居中,在返回月份时考虑 numberOfMonths 和 pagedNavigation
createDecade({ dateObj: new CalendarDate(1995, 8, 18), startIndex: -10, endIndex: 10 }) // 返回围绕 dateObj 居中的十年
createDateRange({ start: new CalendarDate(1995, 8, 18), end: new CalendarDate(2005, 8, 18) }) // 返回开始和结束日期之间的日期数组作为 DateValue
createYearRange({ start: new CalendarDate(1995, 8, 18), end: new CalendarDate(2005, 8, 18) }) // 返回开始和结束日期之间的年份数组作为 DateValue