DateTimePicker 日期时间选择器

输入或者从日历与时间段中选择日期时间。

2026
9
4
20
00

值: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 让浮层里的分段按步长增减。

2026
9
10
20
00
'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。

2026
9
4
20
00
'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 三档,与同档输入框等高,浮层里的日历、时间段与按钮使用同一档尺寸。

2026
9
4
20
00
2026
9
4
20
00
2026
9
4
20
00
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 之内的扁平形态。

2026
9
4
20
00
2026
9
4
20
00
2026
9
4
20
00
2026
9
4
20
00
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
—
点击清除