본문으로 건너뛰기

날짜 · 시각

칸에 바로 적을 수도, 달력에서 고를 수도 있습니다. 값은 늘 문자열입니다: 날짜 'YYYY-MM-DD', 시각 'HH:mm'.

칸은 입력칸과 같은 파인 칸 안의 연 · 월 · 일 조각(spinbutton)입니다. 달력은 드롭다운 층(60)에 뜨는 판이고 모서리를 꺾지 않습니다. 고른 날은 primary 면, 오늘은 굵기와 점, 고를 수 없는 날은 가로줄로 말합니다. 시각은 한국어 · 일본어가 24시간, 영어가 12시간이 기본입니다. react-day-picker · date-fns · radix-ui 가 필요합니다.

import
import { DatePicker, DateRangePicker, DateField, Calendar, TimeField, toUtcIso, fromUtcIso } from "@uyoolabs/ui/date";
쓰는 곳
마감일(DatePicker) · 결제 리포트 · 감사 로그 기간(DateRangePicker, 빠른 선택) · 회의 예약 날짜 + 시작 · 끝(DatePicker + TimeField options) · 공지 게시 기간 · 달력 판만 페이지에 둘 때(Calendar).
쓰지 않는 경우
날짜와 시각을 한 칸에 섞지 않습니다(DateField + TimeField 를 나란히). 생일 · 먼 과거를 달력 칸만으로 고르게 하지 않습니다(칸에 적거나 captionLayout="dropdown"). 주말 · 공휴일을 빨갛게 칠하지 않습니다(빨강은 오류). ‘10/11’ 처럼 언어마다 순서가 다른 숫자만의 날짜를 쓰지 않습니다.
시간대
날짜만 고르는 칸은 시간대가 없는 달력 날짜로 주고받습니다. 시각이 붙으면 toUtcIso(날짜, 시각, 시간대)로 UTC 를 보내고, 보여 줄 때 fromUtcIso 로 바꿉니다. 오프셋은 그 날짜의 것이라 서머타임 건너편도 맞습니다. 없는 시각(서머타임 시작 02:30)은 null, 두 번 있는 시각은 앞의 것입니다.

DatePicker

칸 + 달력 단추 + 팝오버 달력. 고르면 닫히고 포커스는 달력 단추로 돌아옵니다.

20261024

값: 2026-10-24

키동작
←·→조각 사이를 옮긴다(연 · 월 · 일, 시 · 분 · 오전/오후)
↑·↓조각 값을 하나씩 바꾼다. 끝에서 돈다(12 → 1, 59 → 0)
숫자자릿수가 차면 다음 조각으로 넘어간다. 12시간 칸의 시에 ‘15’ 는 오후 3시
Backspace조각을 비운다
붙여 넣기‘2026-10-24’ · ‘2026. 10. 24.’ · ‘2026/10/24’ · ‘14:30’ · ‘2:30 PM’ · ‘오후 2:30’ 을 통째로 읽는다
Alt+↓달력(시각 목록)을 연다. 포커스는 고른 날, 없으면 오늘
키동작
←·→하루
↑·↓한 주
Home·End주의 처음 · 끝
PageUp·PageDown이전 · 다음 달(Shift 를 더하면 해)
Enter·Space고른다. 단일은 닫히고 포커스는 달력 단추로
Esc바꾸지 않고 닫는다. 포커스는 달력 단추로
범위Enter 두 번으로 첫 날 · 끝 날, Tab 으로 [적용]. 적용 전 Esc 는 처음 값
↑·↓빠른 선택 목록에서 옮기고 Enter 로 범위를 채운다(적용은 따로)

DateRangePicker

빠른 선택 · 달력(넓으면 두 달) · 바닥 요약. [적용]을 눌러야 값이 바뀝니다.

TimeField

시 · 분(· 오전/오후) 조각. 보는 사람과 다른 시간대면 약어(EDT)를 붙입니다. options 는 30분 목록 단추입니다.

20261024
1430KST
1530KST

보낼 값: 2026-10-24T05:30:00Z

props

소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.

DatePicker

prop타입기본값설명
valueCalendarDate | null—고른 날짜(제어). 'YYYY-MM-DD' 이고 시간대가 없다. 비었거나 잘못된 값이면 null
defaultValueCalendarDate | nullnull처음 날짜(비제어)
onValueChange(value: CalendarDate | null) => void—바른 날짜가 되거나 비었을 때 부른다. 없는 날 · 범위 밖은 null 로 알리고 칸은 오류 문장을 보인다
minCalendarDate—고를 수 있는 첫날
maxCalendarDate—고를 수 있는 끝날
isDisabled(date: CalendarDate) => boolean—고를 수 없는 날(휴무일 등)
namestring—숨은 input 의 이름. FormData 에 'YYYY-MM-DD' 로 들어간다
requiredboolean—
disabledboolean—
readOnlyboolean—읽기 전용 면. 조각은 포커스를 받지만 바뀌지 않는다
invalidboolean—바깥(Field)이 정한 오류. 경계를 bad 로 그리고 칸의 오류 문장은 Field 에 맡긴다
lang"ko" | "en" | "ja"—요일 · 칸 순서 · 문구의 언어
labelsPartial<DatePickerLabels>—내장 문구 중 바꿀 것만
idstring—Field 가 넘기는 id. 그 <label for> 가 칸의 이름이 된다
aria-describedbystring—
aria-invalidboolean | "true" | "false"—
aria-requiredboolean | "true" | "false"—
aria-labelstring—
aria-labelledbystring—
classNamestring—
weekStartsOn0 | 1—주의 첫날. 기본은 로캘(ko · ja · en-US 모두 일요일)
captionLayout"label" | "dropdown"—label: 달 이름 글자. dropdown: 연 · 월 고르기(생일 · 먼 과거)
openboolean—달력 열림(제어)
defaultOpenbooleanfalse처음 열림(비제어)
onOpenChange(open: boolean) => void—
todayCalendarDate—오늘. 기본은 보는 사람의 오늘이다. 픽스처 · 테스트에서 고정한다

DateRangePicker

prop타입기본값설명
minCalendarDate—고를 수 있는 첫날
maxCalendarDate—고를 수 있는 끝날
isDisabled(date: CalendarDate) => boolean—고를 수 없는 날(휴무일 등)
namestring—숨은 input 의 이름. FormData 에 'YYYY-MM-DD' 로 들어간다
requiredboolean—
disabledboolean—
readOnlyboolean—읽기 전용 면. 조각은 포커스를 받지만 바뀌지 않는다
invalidboolean—바깥(Field)이 정한 오류. 경계를 bad 로 그리고 칸의 오류 문장은 Field 에 맡긴다
lang"ko" | "en" | "ja""ko"요일 · 칸 순서 · 문구의 언어
labelsPartial<DatePickerLabels>—내장 문구 중 바꿀 것만
idstring—Field 가 넘기는 id. 그 <label for> 가 칸의 이름이 된다
aria-describedbystring—
aria-invalidboolean | "true" | "false"—
aria-requiredboolean | "true" | "false"—
aria-labelstring—
aria-labelledbystring—
classNamestring—
weekStartsOn0 | 1—주의 첫날. 기본은 로캘(ko · ja · en-US 모두 일요일)
openboolean—달력 열림(제어)
defaultOpenbooleanfalse처음 열림(비제어)
onOpenChange(open: boolean) => void—
todayCalendarDate—오늘. 기본은 보는 사람의 오늘이다. 픽스처 · 테스트에서 고정한다
valueDateRange—적용한 기간(제어)
defaultValueDateRange{ from: null, to: null }처음 기간(비제어)
onValueChange(range: DateRange) => void—적용을 눌렀을 때만 부른다. 두 번 눌러 고르는 동안에는 부르지 않는다
presetsDateRangePreset[] | false—빠른 선택. 기본: 오늘 · 어제 · 지난 7일 · 지난 30일 · 이번 달 · 지난달 + 사용자 지정. false 면 없다
months1 | 2—나란히 보일 달 수. 기본은 화면 폭이 640 이상이면 2, 아니면 1

DateField

prop타입기본값설명
valueCalendarDate | null—고른 날짜(제어). 'YYYY-MM-DD' 이고 시간대가 없다. 비었거나 잘못된 값이면 null
defaultValueCalendarDate | null—처음 날짜(비제어)
onValueChange(value: CalendarDate | null) => void—바른 날짜가 되거나 비었을 때 부른다. 없는 날 · 범위 밖은 null 로 알리고 칸은 오류 문장을 보인다
minCalendarDate—고를 수 있는 첫날
maxCalendarDate—고를 수 있는 끝날
isDisabled(date: CalendarDate) => boolean—고를 수 없는 날(휴무일 등)
namestring—숨은 input 의 이름. FormData 에 'YYYY-MM-DD' 로 들어간다
requiredboolean—
disabledboolean—
readOnlyboolean—읽기 전용 면. 조각은 포커스를 받지만 바뀌지 않는다
invalidboolean—바깥(Field)이 정한 오류. 경계를 bad 로 그리고 칸의 오류 문장은 Field 에 맡긴다
lang"ko" | "en" | "ja"—요일 · 칸 순서 · 문구의 언어
labelsPartial<DatePickerLabels>—내장 문구 중 바꿀 것만
idstring—Field 가 넘기는 id. 그 <label for> 가 칸의 이름이 된다
aria-describedbystring—
aria-invalidboolean | "true" | "false"—
aria-requiredboolean | "true" | "false"—
aria-labelstring—
aria-labelledbystring—
classNamestring—

Calendar

prop타입기본값설명
minCalendarDate—고를 수 있는 첫날. 그 앞은 고를 수 없는 날(가로줄)이고, 그 달 앞으로는 넘기지 못한다
maxCalendarDate—고를 수 있는 끝날
isDisabled(date: CalendarDate) => boolean—고를 수 없는 날(휴무일 등). 날 칸은 흐린 글자 + 가로줄이고 aria-disabled 다
weekStartsOn0 | 1—주의 첫날. 기본은 로캘(ko · ja · en-US 모두 일요일)
captionLayout"label" | "dropdown"—label: 달 이름 글자. dropdown: 연 · 월 고르기(생일 · 먼 과거)
todayCalendarDate—오늘. 기본은 보는 사람의 오늘이다. 픽스처 · 테스트에서 고정한다
defaultMonthCalendarDate—처음 보일 달(그 날짜가 든 달). 기본은 고른 날, 없으면 오늘
monthCalendarDate—보이는 달(제어). 그 날짜가 든 달을 보인다
onMonthChange(month: CalendarDate) => void—이전 · 다음 달로 넘기거나 키보드로 달을 건넜을 때. 그 달의 1일
numberOfMonths1 | 2—나란히 보일 달 수
autoFocusboolean—처음 그릴 때 고른 날(없으면 오늘)에 포커스를 둔다. 팝오버로 열 때 쓴다
lang"ko" | "en" | "ja"—내장 문구의 언어. 요일 · 달 이름도 이 언어다
labelsPartial<DatePickerLabels>—내장 문구 중 바꿀 것만
classNamestring—
onPreviewChange(date: CalendarDate | null) => void—범위를 고르는 중 손이나 키보드 포커스가 놓인 날. 미리보기 띠를 거기까지 그린다
mode필수"single" | "range"—
selectedCalendarDate | null | DateRange—
onSelect(date: CalendarDate) => void | (range: DateRange) => void—날을 누를 때마다 부른다. 첫 날을 누르면 to 가 null 이다

TimeField

prop타입기본값설명
valueWallTime | null—벽시계 시각(제어). 'HH:mm' 24시간
defaultValueWallTime | nullnull처음 시각(비제어)
onValueChange(value: WallTime | null) => void—바른 시각이 되거나 비었을 때 부른다. 다 채우지 않았거나 없는 시각이면 null
hourCycle12 | 24—기본: 로캘(ko 24 · ja 24 · en 12, 결정 #46)
stepnumber1분 조각 ↑ ↓ 단위
minWallTime—
maxWallTime—
timeZonestring—표시 시간대(IANA). 기본은 보는 사람의 시간대
showTimeZoneboolean | "auto""auto"시간대 약어를 칸 끝에 붙일지. "auto" 는 보는 사람의 시간대와 timeZone 이 다를 때만
referenceDateCalendarDate—이 날짜 기준으로 약어 · 서머타임을 본다. 없으면 오늘
optionsboolean | { optionStep?: number; start?: WallTime }—30분 단위 목록 단추. 끝 시각이면 start 를 주어 걸린 시간을 옆에 단다
namestring—숨은 input 의 이름. FormData 에 'HH:mm'
requiredboolean—
disabledboolean—
readOnlyboolean—
invalidboolean—바깥(Field)이 정한 오류
lang"ko" | "en" | "ja""ko"
labelsPartial<TimeFieldLabels>—
idstring—
aria-describedbystring—
aria-invalidboolean | "true" | "false"—
aria-requiredboolean | "true" | "false"—
aria-labelstring—
aria-labelledbystring—
classNamestring—