值:2026-09-04T20:00
'use client'
import { useState } from 'react'
import { DateTimePicker, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [at, setAt] = useState<string | null>('2026-09-04T20:00')
return (
<Stack gap="sm" align="start">
<DateTimePicker
value={at}
onValueChange={setAt}
clearable
aria-label="发布时间"
className="w-80"
/>
<Text tone="muted" size="sm">
值:{at ?? '空'}
</Text>
</Stack>
)
}
tsx
用法
import { DateTimePicker } from '@hina-ui/react'
ts
日期时间选择器把精度到分的 DateField 与浮层里的 Calendar、TimeField 组合在一起:可以直接在各段里输入,也可以点击末尾的按钮打开浮层,在日历里选日期、在时间段里改时间。value / onValueChange 绑定 YYYY-MM-DDTHH:mm 格式的 ISO 8601 字符串,精度到秒时是 YYYY-MM-DDTHH:mm:ss,与浏览器原生的日期时间控件相同。未声明的属性都会传给包裹各段的组元素,请用 aria-label 或者 aria-labelledby 命名。
值:空
'use client'
import { useState } from 'react'
import { DateTimePicker, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [at, setAt] = useState<string | null>(null)
return (
<Stack gap="sm" align="start">
<DateTimePicker
value={at}
onValueChange={setAt}
placeholder="2026-09-01T09:00"
aria-label="定时发布"
className="w-80"
/>
<Text tone="muted" size="sm">
值:{at ?? '空'}
</Text>
</Stack>
)
}
tsx
示例
范围与不可选的日期
min 与 max 以完整的日期时间约束输入,日历按它们的日期部分禁用日期;unavailable 把某些日期标为不可选;minuteStep 让浮层里的分段按步长增减。
'use client'
import { useState } from 'react'
import { DateTimePicker } from '@hina-ui/react'
export default function Demo() {
const [at, setAt] = useState<string | null>('2026-09-10T20:00')
const weekend = (value: string) => [0, 6].includes(new Date(`${value}T00:00`).getDay())
return (
<DateTimePicker
value={at}
onValueChange={setAt}
min="2026-09-01T00:00"
max="2026-09-30T23:59"
minuteStep={15}
unavailable={weekend}
aria-label="活动时间"
className="w-80"
/>
)
}
tsx
可清除
clearable 在各段之后加一个清除按钮,清空后值为 null。
'use client'
import { useState } from 'react'
import { DateTimePicker } from '@hina-ui/react'
export default function Demo() {
const [at, setAt] = useState<string | null>('2026-09-04T20:00')
return (
<DateTimePicker
value={at}
onValueChange={setAt}
clearable
aria-label="发布时间"
className="w-80"
/>
)
}
tsx
尺寸
size 有 sm、md、lg 三档,与同档输入框等高,浮层里的日历、时间段与按钮使用同一档尺寸。
import { DateTimePicker, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-80">
<DateTimePicker size="sm" value="2026-09-04T20:00" aria-label="小号" />
<DateTimePicker size="md" value="2026-09-04T20:00" aria-label="中号" />
<DateTimePicker size="lg" value="2026-09-04T20:00" aria-label="大号" />
</Stack>
)
}
tsx
状态
invalid 给输入面加上警示色,disabled 禁用整组,readOnly 只读。variant="secondary" 是放在 surface 之内的扁平形态。
import { DateTimePicker, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-80">
<DateTimePicker value="2026-09-04T20:00" invalid aria-label="校验未通过" />
<DateTimePicker value="2026-09-04T20:00" disabled aria-label="已禁用" />
<DateTimePicker value="2026-09-04T20:00" readonly aria-label="只读" />
<DateTimePicker value="2026-09-04T20:00" variant="secondary" aria-label="扁平形态" />
</Stack>
)
}
tsx
在表单中
放进 FormField 后,标签关联到输入区,错误信息由字段渲染;校验规则与提交交给 Form。
'use client'
import { useState } from 'react'
import * as v from 'valibot'
import { Button, DateTimePicker, Form, FormField, Text } from '@hina-ui/react'
const schema = v.object({
startsAt: v.string('请选择开始时间'),
})
export default function Demo() {
const [values, setValues] = useState<{ startsAt: string | null }>({ startsAt: null })
const [saved, setSaved] = useState('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
setSaved(JSON.stringify(data))
}
return (
<Form values={values} rules={schema} className="w-80" onSubmit={save}>
{({ submitting }) => (
<>
<FormField name="startsAt" label="开始时间" required>
<DateTimePicker
value={values.startsAt}
onValueChange={startsAt => setValues({ startsAt })}
/>
</FormField>
<Button type="submit" loading={submitting} className="self-start">
保存
</Button>
{saved && (
<Text tone="muted" size="sm">
已保存:{saved}
</Text>
)}
</>
)}
</Form>
)
}
tsx
行为
- 点击末尾的按钮打开浮层,焦点落在当前选中的日期。选一天只更新日期部分,时间部分沿用原值,浮层保持打开;没有值时,时间部分取
placeholder的时间,没有则取零点。 - 浮层里的时间段直接改时间部分。点击「确定」关闭浮层,焦点回到按钮;点击浮层以外的任何地方或者按 Esc 也会关闭,值已经在每一步写回,不需要确认。
- 浮层是模态的:打开期间焦点保持在浮层里,页面停止滚动。在时间段里按 Esc 不会关闭浮层,请点击「确定」或者浮层以外。
- 浮层在输入面下方展开,与输入面的左缘对齐。
无障碍
- 各段、日历与时间段的无障碍语义分别见
DateField、Calendar与TimeField,浮层里的时间段带有语言包给出的名称。 - 打开浮层的按钮带有语言包给出的名称与
aria-expanded。 - 通过
aria-label或者aria-labelledby为整组命名。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | null | null | YYYY-MM-DDTHH:mm,精度到秒时到 ss |
open | boolean | false | 浮层是否打开, open / onOpenChange |
placeholder | string | 今天 | 空值时各段与日历的起点,其时间部分也是选日期后的默认时间 |
min | string | — | 可选范围的下限 |
max | string | — | 可选范围的上限 |
granularity | 'minute' | 'second' | 'minute' | 精度 |
hourCycle | 12 | 24 | 随语言 | 小时制 |
minuteStep | number | — | 浮层里分段的步长 |
unavailable | (date: string) => boolean | — | 判定某一天是否不可选 |
weekStartsOn | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 随语言 | 一周从周几开始 |
weekdayFormat | 'narrow' | 'short' | 'narrow' | 星期名称的格式 |
fixedWeeks | boolean | true | 日历是否固定显示六周 |
clearable | boolean | false | 是否显示清除按钮 |
readOnly | boolean | false | 是否只读 |
name | string | — | 表单字段名 |
variant | 'primary' | 'secondary' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
disabled | boolean | false | 是否禁用 |
invalid | boolean | false | 是否处于校验未通过状态 |
className | string | — | 追加至根元素的类名 |
内容属性
| 属性 | 说明 |
|---|---|
leading | 前置附属格的内容 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onValueChange | value: string | null | 值变化 |
onOpenChange | open: boolean | 浮层开合变化 |
onClear | — | 点击清除 |