Date and Time Picker
DayPicker does not include a built-in time picker. However, you can implement a time picker by combining DayPicker with a time input field or any custom time picker component, as shown in the following example.
Keep date and time in sync
Derive the time input value from the selected date so the UI stays synchronized, even when the date changes programmatically.
import React, { type ChangeEventHandler, useEffect, useState } from "react";
import { format, setHours, setMinutes } from "date-fns";
import { DayPicker } from "react-day-picker";
export function InputTime() {
const [selected, setSelected] = useState<Date>();
const [timeValue, setTimeValue] = useState<string>("00:00");
// Keep the time input in sync when the selected date changes elsewhere.
useEffect(() => {
if (selected) {
setTimeValue(format(selected, "HH:mm"));
}
}, [selected]);
const handleTimeChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const time = e.target.value;
if (!selected) {
// Defer composing a full Date until a day is picked.
setTimeValue(time);
return;
}
const [hours, minutes] = time.split(":").map((str) => parseInt(str, 10));
// Compose a new Date using the current day plus the chosen time.
const newSelectedDate = setHours(setMinutes(selected, minutes), hours);
setSelected(newSelectedDate);
setTimeValue(time);
};
const handleDaySelect = (date: Date | undefined) => {
if (!timeValue || !date) {
setSelected(date);
return;
}
const [hours, minutes] = timeValue
.split(":")
.map((str) => parseInt(str, 10));
const newDate = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
hours,
minutes,
);
setSelected(newDate);
};
return (
<div>
<form style={{ marginBlockEnd: "1em" }}>
<label>
Set the time:{" "}
<input type="time" value={timeValue} onChange={handleTimeChange} />
</label>
</form>
<DayPicker
mode="single"
selected={selected}
onSelect={handleDaySelect}
footer={`Selected date: ${selected ? selected.toLocaleString() : "none"}`}
/>
</div>
);
}
Time zones and daylight saving
Date values are interpreted in the user's local time zone. If you need to anchor to a specific time zone, create dates with TZDate and convert to strings (such as ISO) when persisting. Be cautious around daylight saving transitions, where some wall-clock times may be skipped or repeated.
Validate time input
Use native <input type="time"> validation or guard values before composing a date:
const MIN_TIME = "09:00";
const MAX_TIME = "18:00";
const handleTimeChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const time = e.target.value;
if (time < MIN_TIME || time > MAX_TIME) return;
setTimeValue(time);
if (selected) {
const [hours, minutes] = time.split(":").map((str) => parseInt(str, 10));
setSelected(setHours(setMinutes(selected, minutes), hours));
}
};
Date ranges with independent times
When selecting a range, keep start and end times separate and merge them into each date:
const [from, setFrom] = useState<Date>();
const [to, setTo] = useState<Date>();
const [fromTime, setFromTime] = useState("09:00");
const [toTime, setToTime] = useState("17:00");
const applyTime = (date: Date | undefined, time: string) => {
if (!date) return undefined;
const [h, m] = time.split(":").map((str) => parseInt(str, 10));
return setHours(setMinutes(date, m), h);
};
<DayPicker
mode="range"
selected={{ from, to }}
onSelect={(range) => {
setFrom(applyTime(range?.from, fromTime));
setTo(applyTime(range?.to, toTime));
}}
/>;
Using custom time pickers
Swap the <input type="time"> with your design system's time picker or dropdowns. Forward aria-* props to keep accessibility consistent with DayPicker, and reuse the same date-composition logic shown above.
import { DayPicker } from "react-day-picker";
import { TimePicker } from "your-design-system"; // placeholder
function CustomTimePicker() {
const [selected, setSelected] = useState<Date>();
const applyTime = (value: string) => {
if (!selected) return;
const [h, m] = value.split(":").map((str) => parseInt(str, 10));
setSelected(setHours(setMinutes(selected, m), h));
};
return (
<>
<DayPicker mode="single" selected={selected} onSelect={setSelected} />
<TimePicker
aria-label="Select time"
value={selected ? format(selected, "HH:mm") : "09:00"}
onChange={applyTime}
/>
</>
);
}
Persisting values
When saving or sending the selection, prefer serializing to an ISO string or UTC timestamp instead of locale strings to avoid time zone ambiguity.
const payload = selected
? { startsAtIso: selected.toISOString(), startsAtMs: selected.getTime() }
: null;