/** * Date-math helpers shared by the E2E specs. The whole point of this module * is that no spec ever hardcodes a year: every date used in an assertion is * derived, at run time, from "today" or from the seeded child's birth date. * * All dates here are UTC-normalized `Date`s at midnight and ISO date * strings (`YYYY-MM-DD`) — the same representation the app's calendar * grid (`CalendarDay.tsx`) uses (`new Date(date + 'T00:00:00Z')`). */ export function toIsoDate(date: Date): string { return date.toISOString().slice(0, 10) } export function addDays(date: Date, days: number): Date { const result = new Date(date.getTime()) result.setUTCDate(result.getUTCDate() + days) return result } /** * Easter Sunday for `year`, via the Anonymous Gregorian (Meeus/Jones/ * Butcher) algorithm. Pure date math, no external calendar data — this is * intentionally independent of the backend's `swedish_calendar.json` cache, * so the test computes its own expectation rather than asking the system * under test what the system under test should show. */ export function easterSunday(year: number): Date { const a = year % 19 const b = Math.floor(year / 100) const c = year % 100 const d = Math.floor(b / 4) const e = b % 4 const f = Math.floor((b + 8) / 25) const g = Math.floor((b - f + 1) / 3) const h = (19 * a + b - d - g + 15) % 30 const i = Math.floor(c / 4) const k = c % 4 const l = (32 + 2 * e + 2 * i - h - k) % 7 const m = Math.floor((a + 11 * h + 22 * l) / 451) const monthPlusDayEncoded = h + l - 7 * m + 114 const month = Math.floor(monthPlusDayEncoded / 31) // 3 = March, 4 = April const day = (monthPlusDayEncoded % 31) + 1 return new Date(Date.UTC(year, month - 1, day)) } /** The first Easter Sunday on or after `from` (whole-day comparison). */ export function nextEasterOnOrAfter(from: Date): Date { let year = from.getUTCFullYear() let candidate = easterSunday(year) while (candidate.getTime() < from.getTime()) { year += 1 candidate = easterSunday(year) } return candidate } /** Whit Sunday (Pingstdagen) is always exactly 49 days after Easter Sunday. */ export function whitSunday(easter: Date): Date { return addDays(easter, 49) } /** Monday of the ISO week containing `date` (Mon=0 .. Sun=6 offset). */ export function mondayOfWeek(date: Date): Date { const isoWeekday = (date.getUTCDay() + 6) % 7 // 0 = Monday return addDays(date, -isoWeekday) } /** * Number of Mon–Sun calendar-week rows that the inclusive range * [start, end] touches — i.e. how many `CalendarWeek` rows a band spanning * that range should render across in the app's Monday-start grid * (see `Calendar.tsx`'s `startOffset = (firstDay.getDay() + 6) % 7`). */ export function countWeekRows(start: Date, end: Date): number { const startWeek = mondayOfWeek(start) const endWeek = mondayOfWeek(end) const diffDays = Math.round((endWeek.getTime() - startWeek.getTime()) / 86_400_000) return diffDays / 7 + 1 }