export interface DateTimeBound { date: string hour: string minute: string } export const emptyDateTimeBound = (): DateTimeBound => ({ date: '', hour: '00', minute: '00', }) export function boundToIso(bound: DateTimeBound): string | undefined { if (!bound.date) return undefined const local = new Date( `${bound.date}T${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}:00` ) if (Number.isNaN(local.getTime())) return undefined return local.toISOString() } export function formatBoundLabel(bound: DateTimeBound): string { if (!bound.date) return 'Datum wählen' return `${formatDateDe(bound.date)} ${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}` } export function formatDateDe(isoDate: string): string { const [y, m, d] = isoDate.split('-').map(Number) if (!y || !m || !d) return isoDate return new Date(y, m - 1, d).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', }) } export function pad2(n: number): string { return String(n).padStart(2, '0') } export const HOURS = Array.from({ length: 24 }, (_, i) => pad2(i)) export const MINUTES = Array.from({ length: 60 }, (_, i) => pad2(i)) const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] const MONTHS = [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember', ] export function getWeekdayLabels(): string[] { return WEEKDAYS } export function getMonthLabel(monthIndex: number): string { return MONTHS[monthIndex] ?? '' } /** Kalendertage inkl. Auffüllung für Mo-start Grid */ export function getCalendarDays(year: number, month: number) { const first = new Date(year, month, 1) const last = new Date(year, month + 1, 0) let startPad = first.getDay() - 1 if (startPad < 0) startPad = 6 const days: { date: string; day: number; currentMonth: boolean }[] = [] for (let i = startPad - 1; i >= 0; i--) { const d = new Date(year, month, -i) days.push({ date: toIsoDate(d), day: d.getDate(), currentMonth: false, }) } for (let d = 1; d <= last.getDate(); d++) { days.push({ date: toIsoDate(new Date(year, month, d)), day: d, currentMonth: true, }) } const totalCells = Math.ceil((startPad + last.getDate()) / 7) * 7 let nextDay = 1 while (days.length < totalCells) { const d = new Date(year, month + 1, nextDay++) days.push({ date: toIsoDate(d), day: d.getDate(), currentMonth: false, }) } return days } export function toIsoDate(d: Date): string { return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}` } export function isBoundActive(bound: DateTimeBound): boolean { return Boolean(bound.date) }