| 1234567891011121314151617181920212223242526272829303132333435363738394041424344 |
- /**
- * 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;
- }
|