/** * Formatierung von Zeiten. Bewusst hier und nicht im times-Store: Auch * Komponenten ohne Store-Zugriff zeigen Dauern an, und die Darstellung soll * überall dieselbe sein. */ /** Dauer in Sekunden als `m:ss.s`. */ export function formatDuration(seconds: number | undefined): string { if (seconds == null || !Number.isFinite(seconds) || seconds < 0) return '0:00.0' const m = Math.floor(seconds / 60) const s = (seconds % 60).toFixed(1) return `${m}:${s.padStart(4, '0')}` } /** Beschriftungen der Zeit-Status aus der Collection. */ export const TIME_STATUS_LABEL: Record = { active: 'Läuft', finished: 'Beendet', dnf: 'DNF', dns: 'DNS', dsq: 'DSQ', } /** Zeitpunkt als `TT.MM.JJJJ, HH:MM`. */ export function formatDateTime(iso: string | undefined): string { if (!iso) return '—' return new Date(iso).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' }) } /** Heutiges Datum als `JJJJ-MM-TT` in lokaler Zeit (nicht UTC). */ export function todayISO(): string { const d = new Date() const m = String(d.getMonth() + 1).padStart(2, '0') const day = String(d.getDate()).padStart(2, '0') return `${d.getFullYear()}-${m}-${day}` } /** * Datumsanteil eines PocketBase-Werts. Die kommen als * `2026-09-12 00:00:00.000Z` zurück; für Vergleiche und `` * zählt nur der Tag. */ export function dayOf(value: string | undefined): string { if (!value) return '' return value.slice(0, 10) } /** Ein Tag als `TT.MM.JJJJ`. */ export function formatDay(value: string | undefined): string { const day = dayOf(value) if (!day) return '' const [y, m, d] = day.split('-') return `${d}.${m}.${y}` } /** * Zeitraum eines Events. Ohne Enddatum oder bei gleichem Tag steht nur ein * Datum da; sonst „12.09.2026 – 14.09.2026". */ export function formatDateRange(starts: string | undefined, ends: string | undefined): string { const from = formatDay(starts) const to = formatDay(ends) if (!from && !to) return '' if (!from) return to if (!to || to === from) return from return `${from} – ${to}` } /** * Standardfenster für „neu" in Übersichten. Zwei Wochen, weil ein Trail * selten öfter befahren wird — kürzer wäre die Hervorhebung meist leer. */ export const RECENT_DAYS = 14 /** Liegt der Zeitpunkt innerhalb der letzten `days` Tage? */ export function isRecent(value: string | undefined, days = RECENT_DAYS): boolean { if (!value) return false const then = new Date(value).getTime() if (!Number.isFinite(then)) return false return Date.now() - then <= days * 24 * 60 * 60 * 1000 }