/** * Compute the daily word ID deterministically from the current date. * Timezone: America/New_York (EST/EDT). * * Formula: (daysSinceEpoch mod targetCount) + 1 * Epoch: 2026-01-01 (arbitrary fixed reference) * * Uses Intl.DateTimeFormat to extract the date in the target timezone, * avoiding toLocaleString+re-parse pitfalls that break when the server * timezone differs from America/New_York. */ const EPOCH_DATE = new Date('2026-01-01T00:00:00-05:00'); // EST function getDateInTz(date: Date, tz: string): string { // en-CA locale gives YYYY-MM-DD format return new Intl.DateTimeFormat('en-CA', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit', }).format(date); } function daysBetween(a: string, b: string): number { // Parse YYYY-MM-DD as UTC midnight for consistent arithmetic const da = new Date(a + 'T00:00:00Z').getTime(); const db = new Date(b + 'T00:00:00Z').getTime(); return Math.floor((da - db) / 86_400_000); } function getDaysSinceEpoch(now: Date): number { const nyDate = getDateInTz(now, 'America/New_York'); const epochDate = getDateInTz(EPOCH_DATE, 'America/New_York'); return daysBetween(nyDate, epochDate); } export function getDailyWordId(targetCount: number, now: Date = new Date()): number { if (targetCount <= 0) { throw new Error('targetCount must be positive'); } const days = getDaysSinceEpoch(now); return (days % targetCount) + 1; }