/** * Format an ISO date string (YYYY-MM-DD) for display. * Uses T12:00:00 to avoid UTC timezone off-by-one when converting to local time. * Defaults to 'long' (e.g. "24. Dezember 1943"); pass 'short' for DD.MM.YYYY. */ export function formatDate(isoDate: string, format: 'short' | 'long' = 'long'): string { const date = new Date(isoDate + 'T12:00:00'); if (format === 'short') { return new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }).format(date); } return new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(date); } /** * Format an ISO date string for medium-length display (e.g. "15. Jun. 1920"). * Uses T12:00:00 to avoid UTC timezone off-by-one. * Pass an explicit BCP 47 locale tag to respect the app locale; defaults to 'de-DE'. */ export function formatMCDate(isoDate: string, locale: string = 'de-DE'): string { return new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short', year: 'numeric' }).format(new Date(isoDate + 'T12:00:00')); } /** * Converts an ISO date string (YYYY-MM-DD) to German display format (DD.MM.YYYY). * Returns an empty string for invalid or empty input. */ export function isoToGerman(iso: string): string { if (!iso || !/^\d{4}-\d{2}-\d{2}$/.test(iso)) return ''; const [y, m, d] = iso.split('-'); return `${d}.${m}.${y}`; } /** * Converts a German date string (DD.MM.YYYY) to ISO format (YYYY-MM-DD). * Returns an empty string for invalid or empty input. */ export function germanToIso(german: string): string { const match = german.match(/^(\d{2})\.(\d{2})\.(\d{4})$/); if (!match) return ''; const [, d, m, y] = match; return `${y}-${m}-${d}`; } /** * Formats a raw date string into German DD.MM.YYYY format. * * Handles two modes: * - Pure digit stream (no dots): auto-inserts dots after position 2 and 4 * - Manual dot entry: preserves user-typed dots, pads single-digit day/month, * and overflows extra digits from day→month and month→year */ export function formatGermanDateInput(raw: string): string { if (!raw.includes('.')) { const digits = raw.replace(/\D/g, '').slice(0, 8); if (digits.length <= 2) return digits; if (digits.length <= 4) return `${digits.slice(0, 2)}.${digits.slice(2)}`; return `${digits.slice(0, 2)}.${digits.slice(2, 4)}.${digits.slice(4)}`; } const trailingDot = raw.endsWith('.'); const parts = raw.split('.').map((p) => p.replace(/\D/g, '')); let day = parts[0] ?? ''; let month = parts[1] ?? ''; let year = parts[2] ?? ''; let dayOverflowed = false; if (day.length > 2) { month = day.slice(2) + month; day = day.slice(0, 2); dayOverflowed = true; } let monthOverflowed = false; if (month.length > 2) { year = month.slice(2) + year; month = month.slice(0, 2); monthOverflowed = true; } year = year.slice(0, 4); const afterDay = !dayOverflowed && parts.length >= 2; if (day.length === 1 && (month || (trailingDot && !dayOverflowed))) { day = '0' + day; } if (month.length === 1 && (year || (trailingDot && afterDay && !monthOverflowed))) { month = '0' + month; } if (year) return `${day}.${month}.${year}`; if (month) { const dot2 = trailingDot && afterDay && !monthOverflowed ? '.' : ''; return `${day}.${month}${dot2}`; } const dot1 = trailingDot && !dayOverflowed ? '.' : ''; return `${day}${dot1}`; } /** * Handles a date input event for German-format date fields (DD.MM.YYYY). * Strips non-digits, formats with dots, mutates the input's displayed value, * and returns the display string and its ISO equivalent. */ export function handleGermanDateInput(e: Event): { display: string; iso: string } { const input = e.target as HTMLInputElement; const display = formatGermanDateInput(input.value); input.value = display; return { display, iso: germanToIso(display) }; }