/** * Event start times in Brussels local wall time, converted to UTC the same way as * backend/src/api/calendar.ts (parseEventDates). Keeps admin/public UI aligned with ICS. */ // Parse "HH:MM", "H:MM am/pm", "Hpm" etc. function parseLocalTime(t: string): { h: number; m: number } { const clean = t.trim(); const m24 = clean.match(/^(\d{1,2}):(\d{2})$/); if (m24) return { h: parseInt(m24[1], 10), m: parseInt(m24[2], 10) }; const mAp = clean.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/i); if (mAp) { let h = parseInt(mAp[1], 10); const m = mAp[2] ? parseInt(mAp[2], 10) : 0; if (mAp[3].toLowerCase() === "pm" && h !== 12) h += 12; if (mAp[3].toLowerCase() === "am" && h === 12) h = 0; return { h, m }; } return { h: 18, m: 0 }; } // Brussels is UTC+1 (CET) / UTC+2 (CEST). Same as calendar.ts. const BRUSSELS_OFFSET_HOURS = 1; /** Extract YYYY-MM-DD from stored date (ISO date-only or full ISO datetime). */ export function normalizeMeetupDateKey(dateStr: string): string | null { const s = dateStr?.trim(); if (!s) return null; const dayPart = s.includes("T") ? s.split("T")[0]! : s.slice(0, 10); if (!/^\d{4}-\d{2}-\d{2}$/.test(dayPart)) return null; return dayPart; } /** * Returns event start instant in UTC, or Invalid Date if date/time cannot be parsed. */ export function getMeetupStartUtc(dateStr: string, timeStr: string): Date { const key = normalizeMeetupDateKey(dateStr); if (!key) return new Date(NaN); const parts = key.split("-").map(Number); const year = parts[0]; const month = parts[1]; const day = parts[2]; if (!year || !month || !day) return new Date(NaN); const t = timeStr?.trim() ? timeStr : "00:00"; const timeParts = t.split(/\s*[-–]\s*/); const { h: startH, m: startM } = parseLocalTime(timeParts[0] ?? ""); const utcStartH = startH - BRUSSELS_OFFSET_HOURS; return new Date(Date.UTC(year, month - 1, day, utcStartH, startM, 0)); }