Date & Time Handling
This guide covers best practices for handling dates and times with React Native Jetpack Compose components.
Date Formats
Section titled “Date Formats”Accepted Input Formats
Section titled “Accepted Input Formats”Date components accept multiple formats for value, minDate, maxDate, and initialDisplayedMonth:
// JavaScript Date object<DatePicker value={new Date(2024, 0, 15)} />
// Epoch milliseconds (number)<DatePicker value={1705276800000} />
// ISO date string<DatePicker value="2024-01-15" />
// ISO datetime string<DatePicker value="2024-01-15T00:00:00.000Z" />Output Format
Section titled “Output Format”All onConfirm and onChange callbacks receive JavaScript Date objects:
<DatePicker value={date} onConfirm={(selectedDate: Date | null) => { console.log(selectedDate); // Date object console.log(selectedDate?.getTime()); // Epoch milliseconds console.log(selectedDate?.toISOString()); // ISO string }}/>Working with Dates
Section titled “Working with Dates”Date State Management
Section titled “Date State Management”// Nullable date (no initial selection)const [date, setDate] = useState<Date | null>(null);
// With default valueconst [date, setDate] = useState<Date | null>(new Date());
// With specific dateconst [date, setDate] = useState<Date | null>(new Date(2024, 0, 15));Date Constraints
Section titled “Date Constraints”// Past dates only<DatePicker value={date} onConfirm={setDate} maxDate={new Date()}/>
// Future dates only<DatePicker value={date} onConfirm={setDate} minDate={new Date()}/>
// Date rangeconst minDate = new Date(2024, 0, 1); // Jan 1, 2024const maxDate = new Date(2024, 11, 31); // Dec 31, 2024
<DatePicker value={date} onConfirm={setDate} minDate={minDate} maxDate={maxDate}/>Year Range
Section titled “Year Range”Control the year selector:
// Birth date picker<DatePicker value={birthDate} onConfirm={setBirthDate} yearRange={{ start: 1920, end: new Date().getFullYear() }} maxDate={new Date()}/>
// Appointment scheduler (next 2 years)<DatePicker value={appointmentDate} onConfirm={setAppointmentDate} yearRange={{ start: new Date().getFullYear(), end: new Date().getFullYear() + 2, }} minDate={new Date()}/>Working with Times
Section titled “Working with Times”Time State Management
Section titled “Time State Management”Time is stored in a Date object (only hours and minutes are relevant):
const [time, setTime] = useState<Date | null>(null);
// Create time for specific hour/minuteconst setTimeFromHoursMinutes = (hours: number, minutes: number) => { const d = new Date(); d.setHours(hours, minutes, 0, 0); setTime(d);};
// Extract hours and minutesconst hours = time?.getHours() ?? 0;const minutes = time?.getMinutes() ?? 0;12/24 Hour Format
Section titled “12/24 Hour Format”// Follow system setting (default)<TimePicker value={time} onConfirm={setTime} />
// Force 24-hour format<TimePicker value={time} onConfirm={setTime} is24Hour={true} />
// Force 12-hour format<TimePicker value={time} onConfirm={setTime} is24Hour={false} />Working with Ranges
Section titled “Working with Ranges”Date Ranges
Section titled “Date Ranges”import type { DateRange } from '@mgcrea/react-native-jetpack-compose';
const [dateRange, setDateRange] = useState<DateRange | null>(null);
// Access individual datesconst startDate = dateRange?.startDate;const endDate = dateRange?.endDate;
// Calculate duration in daysconst getDurationDays = (range: DateRange | null): number => { if (!range?.startDate || !range?.endDate) return 0; const ms = range.endDate.getTime() - range.startDate.getTime(); return Math.ceil(ms / (1000 * 60 * 60 * 24));};Time Ranges
Section titled “Time Ranges”import type { TimeRange } from '@mgcrea/react-native-jetpack-compose';
const [timeRange, setTimeRange] = useState<TimeRange | null>(null);
// Access individual timesconst startTime = timeRange?.startTime;const endTime = timeRange?.endTime;
// Calculate duration in minutesconst getDurationMinutes = (range: TimeRange | null): number => { if (!range?.startTime || !range?.endTime) return 0;
const startMinutes = range.startTime.getHours() * 60 + range.startTime.getMinutes(); const endMinutes = range.endTime.getHours() * 60 + range.endTime.getMinutes();
let duration = endMinutes - startMinutes; if (duration < 0) duration += 24 * 60; // Handle overnight
return duration;};Formatting Dates and Times
Section titled “Formatting Dates and Times”Date Formatting
Section titled “Date Formatting”const formatDate = (date: Date | null): string => { if (!date) return '';
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', });};
// "January 15, 2024"Time Formatting
Section titled “Time Formatting”const formatTime = (time: Date | null): string => { if (!time) return '';
return time.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, });};
// "2:30 PM"Date Range Formatting
Section titled “Date Range Formatting”const formatDateRange = (range: DateRange | null): string => { if (!range?.startDate || !range?.endDate) return '';
const options: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric', };
const start = range.startDate.toLocaleDateString('en-US', options); const end = range.endDate.toLocaleDateString('en-US', options);
return `${start} - ${end}`;};
// "Jan 15 - Jan 20"Time Range Formatting
Section titled “Time Range Formatting”const formatTimeRange = (range: TimeRange | null): string => { if (!range?.startTime || !range?.endTime) return '';
const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', };
const start = range.startTime.toLocaleTimeString('en-US', options); const end = range.endTime.toLocaleTimeString('en-US', options);
return `${start} - ${end}`;};
// "9:00 AM - 5:00 PM"Combining Date and Time
Section titled “Combining Date and Time”When you need both date and time selection:
const [eventDate, setEventDate] = useState<Date | null>(null);const [eventTime, setEventTime] = useState<Date | null>(null);
// Combine into single datetimeconst getEventDateTime = (): Date | null => { if (!eventDate || !eventTime) return null;
const combined = new Date(eventDate); combined.setHours(eventTime.getHours()); combined.setMinutes(eventTime.getMinutes()); combined.setSeconds(0); combined.setMilliseconds(0);
return combined;};
<DatePicker label="Event Date" value={eventDate} onConfirm={setEventDate}/><TimePicker label="Event Time" value={eventTime} onConfirm={setEventTime} disabled={!eventDate}/>Timezone Considerations
Section titled “Timezone Considerations”Working with UTC
Section titled “Working with UTC”// Convert local date to UTC for APIconst toUTC = (date: Date): string => { return date.toISOString();};
// Parse UTC from API to local Dateconst fromUTC = (isoString: string): Date => { return new Date(isoString);};Display in Specific Timezone
Section titled “Display in Specific Timezone”const formatInTimezone = (date: Date, timezone: string): string => { return date.toLocaleString('en-US', { timeZone: timezone, year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', });};
// Display in different timezoneformatInTimezone(date, 'America/New_York'); // "Jan 15, 2024, 2:30 PM"formatInTimezone(date, 'Europe/London'); // "Jan 15, 2024, 7:30 PM"Common Patterns
Section titled “Common Patterns”Dependent Date Fields
Section titled “Dependent Date Fields”const [startDate, setStartDate] = useState<Date | null>(null);const [endDate, setEndDate] = useState<Date | null>(null);
<DatePicker label="Start Date" value={startDate} onConfirm={(date) => { setStartDate(date); // Reset end date if it's before new start date if (endDate && date && endDate < date) { setEndDate(null); } }}/><DatePicker label="End Date" value={endDate} onConfirm={setEndDate} minDate={startDate || undefined} disabled={!startDate}/>Recurring Events
Section titled “Recurring Events”const [startDate, setStartDate] = useState<Date | null>(null);const [recurrence, setRecurrence] = useState<string | null>('none');const [endDate, setEndDate] = useState<Date | null>(null);
<DatePicker label="Start Date" value={startDate} onConfirm={setStartDate}/><Picker label="Repeat" value={recurrence} onChange={setRecurrence} options={[ { value: 'none', label: 'Does not repeat' }, { value: 'daily', label: 'Daily' }, { value: 'weekly', label: 'Weekly' }, { value: 'monthly', label: 'Monthly' }, ]}/>{recurrence !== 'none' && ( <DatePicker label="End Repeat" value={endDate} onConfirm={setEndDate} minDate={startDate || undefined} />)}Business Hours
Section titled “Business Hours”interface DayHours { enabled: boolean; timeRange: TimeRange | null;}
const [schedule, setSchedule] = useState<Record<string, DayHours>>({ monday: { enabled: true, timeRange: null }, tuesday: { enabled: true, timeRange: null }, // ...});
{Object.entries(schedule).map(([day, hours]) => ( <View key={day}> <Text>{day}</Text> <TimeRangePicker label="Hours" value={hours.timeRange} onConfirm={(range) => { setSchedule(s => ({ ...s, [day]: { ...s[day], timeRange: range }, })); }} disabled={!hours.enabled} is24Hour={true} /> </View>))}