diff --git a/docs/modules/serie-presenze.md b/docs/modules/serie-presenze.md index 8d06285..4a90ae9 100644 --- a/docs/modules/serie-presenze.md +++ b/docs/modules/serie-presenze.md @@ -316,8 +316,14 @@ nell'ordine in cui arrivano dalla query (`.order("data")`), quindi non determini loro. Irrilevante finché un buco e una presenza nello stesso giorno danno lo stesso risultato finale, ma va sistemato se un giorno serve l'ordine esatto. -**Il fuso è quello del client.** `oggi` nasce da `new Date().toISOString()`, cioè UTC: nelle -prime ore della giornata italiana un evento di oggi può risultare "non ancora passato". +**`oggi` è sempre in fuso Italia.** `dataOggi()` (`src/lib/scout-live.ts`) usa +`Intl.DateTimeFormat` con `timeZone: "Europe/Rome"`, non i getter locali di `Date` né +`toISOString()`: il cambio ora legale/solare lo gestisce il database IANA dei fusi, non un +offset scritto a mano. È lo stesso `oggi` di `serieConsecutiva()`, `serieConferme()` e del +conteggio presenze — prima `serieConsecutiva()`/`serieConferme()` calcolavano `oggi` con +`toISOString()` (sempre UTC) mentre il conteggio presenze usava i getter locali di `Date` +(corretti solo se il processo gira già in fuso italiano): nelle prime ore della giornata +italiana potevano non essere d'accordo su cosa fosse "oggi". --- diff --git a/src/lib/presenze.ts b/src/lib/presenze.ts index e24d4c8..6ca3a74 100644 --- a/src/lib/presenze.ts +++ b/src/lib/presenze.ts @@ -77,7 +77,7 @@ export function serieConsecutiva( eventi: Evento[], presenze: MappaPresenze, tipo?: "partita" | "allenamento", - oggi: string = oggiIso(), + oggi: string = dataOggi(), ): number { return serieSu( giocatoreId, @@ -102,7 +102,7 @@ export function serieConferme( giocatoreId: string, eventi: Evento[], tempi: MappaTempiRisposta, - oggi: string = oggiIso(), + oggi: string = dataOggi(), ): number { return serieSu( giocatoreId, @@ -116,10 +116,6 @@ export function serieConferme( ); } -function oggiIso() { - return new Date().toISOString().slice(0, 10); -} - /** Scorre gli eventi già passati in ordine di data applicando la regola delle serie. */ function serieSu( giocatoreId: string, diff --git a/src/lib/scout-live.ts b/src/lib/scout-live.ts index b3b7fd4..2e739db 100644 --- a/src/lib/scout-live.ts +++ b/src/lib/scout-live.ts @@ -6,9 +6,15 @@ import { useEventi, type Evento } from "./eventi"; /** Minuti dopo i quali una sessione scout inattiva viene considerata libera. */ export const SCADENZA_MINUTI = 5; -export function dataOggi(): string { - const d = new Date(); - return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; +/** + * Data di oggi in Italia (`Europe/Rome`, con cambio ora legale/solare gestito dal database + * IANA dei fusi orari). Non i getter locali di `Date` — quelli seguono il fuso del processo + * che esegue il codice: sul client di chi è in Italia coincide, ma lato server (SSR, dev + * container) è spesso UTC, e `toISOString()` lo è sempre. Sbagliare qui sposta la mezzanotte: + * nella prima ora o due della giornata italiana, eventi già passati risulterebbero "di domani". + */ +export function dataOggi(adesso: Date = new Date()): string { + return new Intl.DateTimeFormat("en-CA", { timeZone: "Europe/Rome" }).format(adesso); } /** La partita in programma oggi, se c'è. */ diff --git a/test/unit/scout-live.test.ts b/test/unit/scout-live.test.ts index 6b31ed5..3908d10 100644 --- a/test/unit/scout-live.test.ts +++ b/test/unit/scout-live.test.ts @@ -23,9 +23,23 @@ const evento = (id: string, data: string, tipo: Evento["tipo"]): Evento => ({ pagelleChiuse: false, }); -// --- dataOggi ---------------------------------------------------------------- +// --- dataOggi ------------------------------------------------------------------ assert.match(dataOggi(), /^\d{4}-\d{2}-\d{2}$/); -assert.equal(dataOggi(), new Date().toLocaleDateString("sv-SE"), "data locale, non UTC"); +// CET (gennaio, UTC+1): dopo le 23:00 UTC del 15, a Roma è già il 16. L'ISO in UTC +// direbbe ancora 15: se `dataOggi()` tornasse a farlo, questo test lo scoprirebbe. +assert.equal( + dataOggi(new Date("2026-01-15T23:30:00Z")), + "2026-01-16", + "CET: mezzanotte italiana precede quella UTC di un'ora", +); +// CEST (luglio, UTC+2): lo scarto raddoppia, la mezzanotte italiana anticipa quella UTC +// di due ore. Se il calcolo usasse un offset fisso invece del fuso Europe/Rome, questo +// secondo caso lo tradirebbe anche se il primo passasse per caso. +assert.equal( + dataOggi(new Date("2026-07-15T22:30:00Z")), + "2026-07-16", + "CEST: il cambio ora legale porta lo scarto a due ore, non resta fisso a uno", +); // --- partitaDiOggi ----------------------------------------------------------- const eventi = [