fix(social-image): Uhrzeit/Datum in Europe/Berlin rendern
Deploy / verify (push) Successful in 1m14s
Deploy / deploy (push) Successful in 2m8s

Social-Image-Generierung lief SSR auf Server-TZ (UTC) → terminZeit
"10:00+02:00" wurde als "08:00" aufs Bild gerendert (−2h im Sommer).
toLocaleDate/TimeString in Kalender- und Post-Route auf timeZone
"Europe/Berlin" gepinnt. Cache-Version v26→v27 invalidiert alte PNGs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-06-19 13:20:01 +02:00
parent 781869b2b4
commit fdc18ab5b8
2 changed files with 10 additions and 4 deletions
@@ -97,18 +97,23 @@ async function loadFonts() {
return fontCache; return fontCache;
} }
// terminZeit ist mit Offset gespeichert (z. B. 10:00+02:00 = 08:00 UTC).
// Rendering muss in Europe/Berlin erzwungen werden — sonst rendert der
// SSR-Prozess (Server-TZ = UTC) 2h zu früh (10 Uhr → 8 Uhr auf dem Bild).
const TZ = "Europe/Berlin";
function formatDate(iso: string | undefined): { date: string; time: string | null } { function formatDate(iso: string | undefined): { date: string; time: string | null } {
if (!iso) return { date: "", time: null }; if (!iso) return { date: "", time: null };
const d = new Date(iso); const d = new Date(iso);
const date = d.toLocaleDateString("de-DE", { day: "2-digit", month: "long", year: "numeric" }); const date = d.toLocaleDateString("de-DE", { day: "2-digit", month: "long", year: "numeric", timeZone: TZ });
const hasTime = !/T00:00:00/.test(iso); const hasTime = !/T00:00:00/.test(iso);
const time = hasTime ? d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }) : null; const time = hasTime ? d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit", timeZone: TZ }) : null;
return { date, time }; return { date, time };
} }
function weekday(iso: string | undefined): string { function weekday(iso: string | undefined): string {
if (!iso) return ""; if (!iso) return "";
return new Date(iso).toLocaleDateString("de-DE", { weekday: "long" }); return new Date(iso).toLocaleDateString("de-DE", { weekday: "long", timeZone: TZ });
} }
/** /**
@@ -139,7 +144,7 @@ async function loadEventImage(
const cache = new Map<string, { png: Buffer; ts: number }>(); const cache = new Map<string, { png: Buffer; ts: number }>();
const CACHE_TTL = 10 * 60_000; // 10min const CACHE_TTL = 10 * 60_000; // 10min
const CACHE_VERSION = "v26"; const CACHE_VERSION = "v27";
function parseFormat(v: string | null): Format { function parseFormat(v: string | null): Format {
if (v === "square") return "square"; if (v === "square") return "square";
@@ -90,6 +90,7 @@ function formatDateLong(iso: string | undefined | null): string {
day: "2-digit", day: "2-digit",
month: "long", month: "long",
year: "numeric", year: "numeric",
timeZone: "Europe/Berlin",
}); });
} catch { } catch {
return ""; return "";