TimeRangeInput

A calendar component for date selection with support for single, range, and multiple date picking

Installation

Terminal
pnpm add @choice-ui/calendar

Import

Component.tsx
import { type MonthCalendarProps, MonthCalendar, DateInput, DateRangeInput, type TimeCalendarProps, TimeCalendar, TimeInput, TimeRangeInput, type QuarterCalendarProps, QuarterCalendar, type YearCalendarProps, YearCalendar, dateUtils, generateWeekdayNames, generateCalendarDays, formatMonthTitle, calculateWeekNumbers, inferSelectionMode, inferMonthFromValue, isCalendarValueEqual, LOCALE_MAP, resolveLocale, getSupportedLocales, isChineseLocale, type DateComparisonMode, type DateParts, createTimeZoneContext, isSameDayInTimeZone, isSameMonthInTimeZone, isSameYearInTimeZone, isSameWeekInTimeZone, isTodayInTimeZone, isWithinRange, getDateParts, getDateKey, areDatesEqual, clearTimeZoneCache, parseMonthName, englishMonths, chineseMonths, validateDateRange, validateTimeRange, isValidDateExists, getLastDayOfMonth, smartCorrectDate, smartCorrectYear, quickValidateDate, handleShortcuts, parseRelativeDate, parseExtendedRelativeDate, parseNaturalLanguage, getLocaleKey, parseEnglishDate, getLocale, detectDateFormat, type ParseResult, type DetailedParseResult, type ParseOptions, parseDate, parseDate, parseDate, getPredictionInfo, tryRelaxedParsing, smartParseDate, parseDate, tryRelaxedParsing, smartParseDate, getPredictionInfo, type ParseOptions, type ParseResult, type DetailedParseResult, getEnhancedPrediction, type PredictionResult, validateDateRange, validateTimeRange, isValidDateExists, smartCorrectDate, smartCorrectYear, quickValidateDate, getLastDayOfMonth, getLocale, detectDateFormat, parseMonthName, parseEnglishDate, parseRelativeDate, parseExtendedRelativeDate, parseNaturalLanguage, getLocaleKey, handleShortcuts, extractDigits, convertTwoDigitYear, isValidMonthDay, canBeMonthDay, isReasonableYear, type NumericParseResult, parseYYMMDD, parseYYYYMMDD, parse3Digits, yearUtils, quarterUtils, getQuarterMonths, createQuarter, getCurrentQuarter, getYearQuarters, isQuarterEqual, formatQuarter, getQuarterDateRange, dateToTimeString, timeStringToDate, normalizeTimeValue, smartParseTimeValue, tryRelaxedTimeParsing, createTimeValue, timeToDate, dateToTime, formatTimeValue, formatHourValue, formatMinuteValue, generateHourOptions, generateMinuteOptions, isTimeEqual, findClosestValidTime, generateTimeOptions, createTimeToday, defaultLocaleMap, naturalLanguageMap, relativeDatePatterns, commonDateFormats, commonTimeFormats, parserConfig, parseCache, useDateInput, useTimeInput, type BaseYearProps, type YearItem, type YearLayoutProps, type YearNavigationProps, type BaseQuarterProps, type Quarter, type QuarterItem, type QuarterLayoutProps, type QuarterNavigationProps, type BaseCalendarProps, type CalendarValue, type CalendarState, type CalendarLayoutProps, type BaseDateProps, type DateInputValue, type DateDataFormat, type DateRange, type DateParserOptions, type DateInteractionProps, type BaseTimeProps, type Time, type TimeInputValue, type TimeDataFormat, type TimeParserOptions, type TimeLayout, type TimeInteractionProps, type TimeOptionItem, type SmartInputOptions, type StepProps, type NaturalLanguageMap, type RelativeDatePattern } from "@choice-ui/calendar"

Basic

Default: Shows the basic TimeRangeInput usage with standard configuration.
- Demonstrates dual time input fields for start and end times. - Uses 24-hour format (HH:mm) as the default time format. - Provides a foundation for time range selection implementation.

Size

Size: Demonstrates the large size variant of TimeRangeInput.
- Shows the component with increased dimensions for prominent interfaces. - Useful for applications requiring larger touch targets or enhanced visibility.

Variable

Variable: Demonstrates the dark theme variant of TimeRangeInput.
- Shows the component styled for dark theme environments. - Useful for applications with dark mode or specialized UI themes.

Range Synchronization

RangeSynchronization: Demonstrates the intelligent time range synchronization feature.
- Shows how start time changes automatically adjust the end time to maintain range length. - Displays boundary validation where end times push start times when necessary. - Includes comprehensive explanation and test scenarios for understanding the behavior.
8 hours
🎯 Time Range Synchronization Logic
Start Time Change:Automatically adjust the end time to maintain the original range length
End Time Change:If the end time is less than or equal to the start time, the start time is pushed to the end position
Dynamic Range:First adjust the end time to set the desired range length, then any changes to the start time will maintain this length
🧪 Test Steps
1. Adjust the end time to 19:00 → the range becomes 10 hours
2. Modify the start time to 10:00 → the end time is automatically adjusted to 20:00 to maintain a 10-hour distance
3. Set the end time to be earlier than the start time (e.g., 08:00) → the start time is pushed to 08:00
4. Support cross-day range: start time 22:00, end time the next day 06:00

With Preset Range

WithPresetRange: Demonstrates TimeRangeInput with pre-filled time values.
- Shows the component with initial start (09:00) and end (17:30) times set. - Displays a typical work schedule as an example of preset configurations. - Useful for forms or interfaces that need default time ranges.

Cross Midnight

CrossMidnight: Demonstrates cross-midnight time range support.
- Shows time ranges that span across midnight (22:00 to 06:00). - Displays proper handling of overnight shifts and 24-hour operations. - Useful for night shift scheduling, security operations, or 24/7 businesses.
💡 Support cross-day time range (e.g., night shift from 22:00 to the next day 06:00)

Different Formats

DifferentFormats: Demonstrates various time format options and their visual representation.
- Shows 24-hour format, 12-hour format with AM/PM, and format with seconds. - Displays how different formats affect the display and input experience. - Useful for understanding format flexibility and regional preferences.

24-hour format (HH:mm)

8 hours

12-hour format (h:mm a)

8 hours

With seconds format (HH:mm:ss)

8 hours

Internationalization

Internationalization: Demonstrates comprehensive multi-language support.
- Shows TimeRangeInput working with Chinese, English, and Japanese locales. - Displays locale-specific time formatting and placeholder text. - Demonstrates proper duration display in multiple languages.

中文 (zh-CN)

8小时30分钟
Duration display: 8h 30m

English (en-US)

8h 30m
Duration display: 8h 30m

日本語 (ja)

8時間30分
Duration display: 8h 30m

Common Scenarios

CommonScenarios: Demonstrates real-world usage scenarios for TimeRangeInput.
- Shows work hours, lunch breaks, exercise time, and night shift examples. - Displays practical applications with appropriate time ranges and contexts. - Useful for understanding when and how to implement time range selection.

🏢 Work Time

9 hours

🍽️ Lunch Time

1 hour

🏃‍♂️ Exercise Time

1 hour

🌙 Night Shift Time

8 hours
💡 Cross-day work, 8 hours

Duration Only

DurationOnly: Demonstrates time range duration calculation and display.
- Shows short duration (45 minutes), full hours (2 hours), and long duration (12 hours). - Displays how the component calculates and presents time range durations. - Useful for understanding duration formatting and time range analysis.

Short Time Range

45 mins

Full Hour Time Range

2 hours

Long Time Range

12 hours

Read Only

ReadOnly: Demonstrates the TimeRangeInput component in readOnly mode.
- Prevents value changes while allowing focus and selection - Maintains normal visual appearance (unlike disabled) - Useful for displaying non-editable time range information
Current Start:
9:00:00 AM
Current End:
5:00:00 PM
Change Count:
0
8 hours
8 hours
💡 Try changing the readonly time range input - the values should not change and the change count should remain at 0. Only the normal input will change the values.

API reference

MonthCalendarTypeDefault
className
string
|
undefined
-
currentMonth
Date
|
undefined
-
dateComparisonMode
undefined
|
"exact-time"
|
"date-only"
-
defaultValue
CalendarValue
|
undefined
-
disabledDates
Date[]
|
undefined
-
fixedGrid
boolean
|
undefined
-
highlightDates
Date[]
|
undefined
-
highlightToday
boolean
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onChange
((value: CalendarValue) => void)
|
undefined
-
onMonthChange
((month: Date) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
selectionMode
undefined
|
"single"
|
"multiple"
|
"range"
-
showOutsideDays
boolean
|
undefined
-
showWeekNumbers
boolean
|
undefined
-
timeZone
string
|
undefined
-
value
CalendarValue
|
undefined
-
weekStartsOn
undefined
|
0
|
1
|
2
|
3
|
4
|
5
|
6
-
weekdayNames
string[]
|
undefined
-
direction
undefined
|
"horizontal"
|
"vertical"
-
variant
undefined
|
"default"
|
"dark"
-
DateInputValueTypeDefault
value
Date
|
null
|
undefined
-
onChange
((date: Date
|
null) => void)
|
undefined
-
format
string
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
enableCache
boolean
|
undefined
true
enableKeyboardNavigation
boolean
|
undefined
true
enablePrediction
boolean
|
undefined
false
enableProfiling
boolean
|
undefined
false
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
prefixElement
ReactNode -
suffixElement
ReactNode -
DateRangeInputTypeDefault
endDisabled
boolean
|
undefined
-
endPlaceholder
string
|
undefined
-
endSuffixElement
ReactNode -
endValue
Date
|
null
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onEndChange
((date: Date
|
null) => void)
|
undefined
-
onEndFocus
(() => void)
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
onStartChange
((date: Date
|
null) => void)
|
undefined
-
onStartFocus
(() => void)
|
undefined
-
rangePrecision
number
|
undefined
1 - Minimum unit is 1 day
startDisabled
boolean
|
undefined
-
startPlaceholder
string
|
undefined
-
startSuffixElement
ReactNode -
startValue
Date
|
null
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
TimeCalendarTypeDefault
className
string
|
undefined
-
hourStep
number
|
undefined
-
minuteStep
number
|
undefined
-
open
boolean
|
undefined
-
onOpenChange
((open: boolean) => void)
|
undefined
-
closeOnSelect
boolean
|
undefined
-
triggerRef
RefObject<HTMLElement>
|
undefined
-
triggerSelector
string
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
offset
number
|
undefined
-
placement
undefined
|
"top"
|
"right"
|
"bottom"
|
"left"
|
"top-start"
|
"top-end"
|
"right-start"
|
"right-end"
|
"bottom-start"
|
"bottom-end"
|
"left-start"
|
"left-end"
-
matchTriggerWidth
boolean
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"reset"
-
TimeInputValueTypeDefault
value
Date
|
null
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
format
string
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
step
number
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
prefixElement
ReactNode -
suffixElement
ReactNode -
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
TimeRangeInputTypeDefault
endDisabled
boolean
|
undefined
-
endPlaceholder
string
|
undefined
-
endSuffixElement
ReactNode -
endValue
Date
|
null
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onEndChange
((time: Date
|
null) => void)
|
undefined
-
onEndFocus
(() => void)
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
onStartChange
((time: Date
|
null) => void)
|
undefined
-
onStartFocus
(() => void)
|
undefined
-
startDisabled
boolean
|
undefined
-
startPlaceholder
string
|
undefined
-
startSuffixElement
ReactNode -
startValue
Date
|
null
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
QuarterCalendarTypeDefault
currentYear
number
|
undefined
-
defaultValue
Quarter
|
undefined
-
disabled
boolean
|
undefined
-
disabledQuarters
{ quarter: number; year: number; }[]
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxYear
number
|
undefined
-
minYear
number
|
undefined
-
onChange
((quarter: Quarter
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
startYear
number
|
undefined
-
value
Quarter
|
null
|
undefined
-
onNavigate
((direction: "prev"
|
"next", newYear: number) => void)
|
undefined
-
className
string
|
undefined
-
variant
undefined
|
"default"
|
"dark"
-
YearCalendarTypeDefault
currentYear
Date
|
undefined
-
defaultValue
Date
|
undefined
-
disabled
boolean
|
undefined
-
disabledYears
Date[]
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxYear
Date
|
undefined
-
minYear
Date
|
undefined
-
onChange
((year: Date
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
startYear
Date
|
undefined
-
value
Date
|
null
|
undefined
-
yearCount
number
|
undefined
-
onNavigate
((direction: "prev"
|
"next", newStartYear: Date) => void)
|
undefined
-
className
string
|
undefined
-
variant
undefined
|
"default"
|
"dark"
-
inferSelectionModeTypeDefault
end
Date -
start
Date -
inferMonthFromValueTypeDefault
end
Date -
start
Date -
formatQuarterTypeDefault
label
string -
months
string[] -
quarter
1
|
2
|
3
|
4
-
year
number -
getQuarterDateRangeTypeDefault
label
string -
months
string[] -
quarter
1
|
2
|
3
|
4
-
year
number -
timeToDateTypeDefault
hour
number -
minute
number -
useDateInputTypeDefault
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
readOnly
boolean
|
undefined
-
ref
Ref<HTMLInputElement>
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onChange
((date: Date
|
null) => void)
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enablePrediction
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
useTimeInputTypeDefault
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
readOnly
boolean
|
undefined
-
ref
Ref<HTMLInputElement>
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-