export interface TimeParts { hours: number minutes: number } export function clampHours(value: number): number { if (Number.isNaN(value)) return 0 return Math.min(23, Math.max(0, Math.trunc(value))) } export function clampMinutes(value: number): number { if (Number.isNaN(value)) return 0 return Math.min(59, Math.max(0, Math.trunc(value))) } export function padSegment(value: number): string { return value.toString().padStart(2, '0') } export function parseTime(value: string | null | undefined): TimeParts | null { if (!value) return null const match = /^(\d{1,2}):(\d{1,2})$/.exec(value.trim()) if (!match) return null return { hours: clampHours(Number.parseInt(match[1], 10)), minutes: clampMinutes(Number.parseInt(match[2], 10)), } } export function formatTime(hours: number, minutes: number): string { return `${padSegment(clampHours(hours))}:${padSegment(clampMinutes(minutes))}` }