export const BERLIN_TIME_ZONE = 'Europe/Berlin'; export type CalendarDay = { date: string; dayNumber: number; inSelectedMonth: boolean; isToday: boolean; }; const berlinDateFormatter = new Intl.DateTimeFormat('en-CA', { timeZone: BERLIN_TIME_ZONE, year: 'numeric', month: '2-digit', day: '2-digit' }); function padded(value: number) { return String(value).padStart(2, '0'); } function dateParts(value: string | Date) { const parts = berlinDateFormatter.formatToParts( typeof value === 'string' ? new Date(value) : value ); const read = (type: Intl.DateTimeFormatPartTypes) => Number(parts.find((part) => part.type === type)?.value); return { year: read('year'), month: read('month'), day: read('day') }; } export function berlinDateKey(value: string | Date) { const parts = dateParts(value); return `${parts.year}-${padded(parts.month)}-${padded(parts.day)}`; } export function berlinMonthKey(value: string | Date) { return berlinDateKey(value).slice(0, 7); } export function normalizeMonthKey(value: string | null | undefined, fallback: string) { if (!value || !/^\d{4}-(0[1-9]|1[0-2])$/.test(value)) return fallback; return value; } export function shiftMonth(monthKey: string, delta: number) { const [year, month] = monthKey.split('-').map(Number); const shifted = new Date(Date.UTC(year, month - 1 + delta, 1)); return `${shifted.getUTCFullYear()}-${padded(shifted.getUTCMonth() + 1)}`; } export function formatMonthLabel(monthKey: string) { const [year, month] = monthKey.split('-').map(Number); return new Intl.DateTimeFormat('de-DE', { timeZone: 'UTC', month: 'long', year: 'numeric' }).format(new Date(Date.UTC(year, month - 1, 1))); } export function buildCalendarDays(monthKey: string, todayKey: string): CalendarDay[] { const [year, month] = monthKey.split('-').map(Number); const first = new Date(Date.UTC(year, month - 1, 1)); const mondayOffset = (first.getUTCDay() + 6) % 7; const start = new Date(Date.UTC(year, month - 1, 1 - mondayOffset)); return Array.from({ length: 42 }, (_, index) => { const date = new Date(start); date.setUTCDate(start.getUTCDate() + index); const dateKey = `${date.getUTCFullYear()}-${padded(date.getUTCMonth() + 1)}-${padded(date.getUTCDate())}`; return { date: dateKey, dayNumber: date.getUTCDate(), inSelectedMonth: dateKey.startsWith(monthKey), isToday: dateKey === todayKey }; }); }