/** * Formats a JS Date as an ISO 8601 string carrying its actual Europe/Berlin * UTC offset (e.g. "2026-06-15T09:00:00+02:00" in summer, * "...+01:00" in winter) — what schema.org's Event startDate/endDate * expect. Uses Intl's timezone database rather than hardcoded +1/+2 math, * so it's correct across the DST transition automatically. */ export function toBerlinOffsetISOString(date: Date): string { const parts = new Intl.DateTimeFormat("en-US", { timeZone: "Europe/Berlin", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23", timeZoneName: "shortOffset", }).formatToParts(date); const get = (type: string) => parts.find((p) => p.type === type)?.value ?? "00"; const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT+0"; const match = /GMT([+-])(\d+)(?::(\d+))?/.exec(tzName); const sign = match?.[1] ?? "+"; const offsetHours = (match?.[2] ?? "0").padStart(2, "0"); const offsetMinutes = (match?.[3] ?? "0").padStart(2, "0"); return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${sign}${offsetHours}:${offsetMinutes}`; }