Datetime naive locali e UTC mescolati nello stesso DB #42

Closed
opened 2026-09-21 21:56:04 +00:00 by Adriano · 4 comments
Owner

FILE: src/backend/services/measurement_service.py (datetime.now()), src/backend/services/production_service.py:40 (datetime.now()), src/backend/services/auth_service.py:74 (utcnow()), func.now() di MySQL su measured_at.
COSA: fusi diversi nelle colonne dello stesso DB; i filtri data della dashboard partono naive dal browser (src/frontend/flask_app/templates/statistics/dashboard.html:315-316): con DB in UTC e browser in CET le misure fra 00:00 e 02:00 cadono nel giorno sbagliato.
PERCHÉ NON È STATO CHIUSO IN #36: uniformare richiede una migrazione dei dati esistenti e una decisione (tutto UTC).
PRESUNTO: il fuso del container MySQL in produzione non è stato verificato.

ORIGINE: residui dichiarati alla chiusura delle issue della revisione del 21/09/2026.

FILE: src/backend/services/measurement_service.py (`datetime.now()`), src/backend/services/production_service.py:40 (`datetime.now()`), src/backend/services/auth_service.py:74 (`utcnow()`), `func.now()` di MySQL su `measured_at`. COSA: fusi diversi nelle colonne dello stesso DB; i filtri data della dashboard partono naive dal browser (src/frontend/flask_app/templates/statistics/dashboard.html:315-316): con DB in UTC e browser in CET le misure fra 00:00 e 02:00 cadono nel giorno sbagliato. PERCHÉ NON È STATO CHIUSO IN #36: uniformare richiede una migrazione dei dati esistenti e una decisione (tutto UTC). PRESUNTO: il fuso del container MySQL in produzione non è stato verificato. ORIGINE: residui dichiarati alla chiusura delle issue della revisione del 21/09/2026.
Adriano added the bug label 2026-09-21 21:56:04 +00:00
Author
Owner

ESITO: richiesta
RIF: gitea:Adriano/TieMeasureFlow#42
COSA: analisi (passo 1). Decisione di Adriano del 06/10/2026: tutto UTC nel DB, conversione in ora italiana solo alla presentazione.
STATO MISURATO: docker-compose.yml e docker-compose.dev.yml non impostano TZ; mysql:8.0 e python-slim partono in UTC. In produzione, quindi, sia func.now() sia datetime.now() scrivono già UTC.
Il difetto vero è la presentazione: il backend serializza date naive senza fuso, e new Date("...T08:00:00") nel browser le legge come ora locale (2 ore di errore in CEST). I filtri della dashboard (statistics/dashboard.html:315) mandano ora locale naive.
In sviluppo (uv run sul PC) datetime.now() è locale mentre CURRENT_TIMESTAMP di SQLite è UTC: lì il mescolamento c'è davvero.
MIGRAZIONE DATI: nessuna. PRESUNTO: i dati esistenti, scritti dentro i container, sono già UTC. Se esiste un'installazione con TZ locale sul container, va spostata a mano una volta.
CONTRATTO fra le due unità: ogni datetime in uscita dalle API è ISO 8601 con fuso UTC esplicito (es. "2026-10-06T08:00:00+00:00"); ogni datetime in ingresso con fuso viene convertito in UTC naive; un ingresso naive si intende UTC.
UNITÀ A (backend):

  • src/backend/utils/tempo.py (nuovo):
    • utcnow() → naive UTC;
    • as_utc_naive(dt): da aware converte in UTC naive, un naive resta com'è;
    • to_local(dt) → ora di settings.display_timezone (config.py, default "Europe/Rome", zoneinfo).
  • Scritture: measurement_service.py:278, production_service.py:40 (anche la docstring di _now), auth_service.py (last_login, e le date che ha aggiunto #41), setup.py:561 → utcnow().
  • report_service.py:345/:433 generated_at = utcnow(), e nei PDF ogni data si mostra con to_local.
  • Nomi file production_export_service.py:194 e measurements.py:362: to_local(utcnow()).
  • Serializzazione: un tipo Annotated UtcDateTime (PlainSerializer che aggiunge tzinfo=UTC ai naive) in src/backend/models/api, usato per TUTTI i campi datetime degli schemi di risposta.
  • Ingressi: date_from/date_to di statistics.py:35/:103, e ogni altro Query/Body datetime, passano da as_utc_naive.
  • docker-compose.yml e docker-compose.dev.yml: aggiungere --default-time-zone=+00:00 al command di mysql.
    UNITÀ B (frontend Flask):
  • app.py: filtro Jinja local_dt(iso) → stringa "gg/mm/aaaa hh:mm" in Europe/Rome (config DISPLAY_TIMEZONE); usarlo in templates/measure/task_complete.html:186 e in ogni template che taglia stringhe ISO.
  • statistics/dashboard.html:315-316: date_from = new Date(dateFrom+'T00:00:00').toISOString() (mezzanotte locale convertita in UTC), date_to = fine giornata locale.
  • JS (recipe_list.html:222, select_recipe.html:262, version_history.html:387, static/js/csv-export.js:213): con il fuso esplicito new Date è già corretto; verificare soltanto e togliere eventuali aggiunte manuali di "Z".
    TEST A: utcnow naive; una risposta con un datetime ha +00:00; date_from con fuso (+02:00) filtra come il suo equivalente UTC; generated_at del report in ora italiana.
    TEST B: local_dt("2026-10-06T08:00:00+00:00") → "06/10/2026 10:00"; il test della dashboard, se esiste, controlla i parametri inviati.
    NON TOCCARE: schema DB (DateTime naive resta); le migrazioni.
    FATTO SE: uv run pytest verde; grep "datetime.now()|utcnow()" in src/backend fuori da utils/tempo.py e dai test → vuoto.
ESITO: richiesta RIF: gitea:Adriano/TieMeasureFlow#42 COSA: analisi (passo 1). Decisione di Adriano del 06/10/2026: tutto UTC nel DB, conversione in ora italiana solo alla presentazione. STATO MISURATO: docker-compose.yml e docker-compose.dev.yml non impostano TZ; mysql:8.0 e python-slim partono in UTC. In produzione, quindi, sia func.now() sia datetime.now() scrivono già UTC. Il difetto vero è la presentazione: il backend serializza date naive senza fuso, e new Date("...T08:00:00") nel browser le legge come ora locale (2 ore di errore in CEST). I filtri della dashboard (statistics/dashboard.html:315) mandano ora locale naive. In sviluppo (uv run sul PC) datetime.now() è locale mentre CURRENT_TIMESTAMP di SQLite è UTC: lì il mescolamento c'è davvero. MIGRAZIONE DATI: nessuna. PRESUNTO: i dati esistenti, scritti dentro i container, sono già UTC. Se esiste un'installazione con TZ locale sul container, va spostata a mano una volta. CONTRATTO fra le due unità: ogni datetime in uscita dalle API è ISO 8601 con fuso UTC esplicito (es. "2026-10-06T08:00:00+00:00"); ogni datetime in ingresso con fuso viene convertito in UTC naive; un ingresso naive si intende UTC. UNITÀ A (backend): - src/backend/utils/tempo.py (nuovo): - utcnow() → naive UTC; - as_utc_naive(dt): da aware converte in UTC naive, un naive resta com'è; - to_local(dt) → ora di settings.display_timezone (config.py, default "Europe/Rome", zoneinfo). - Scritture: measurement_service.py:278, production_service.py:40 (anche la docstring di _now), auth_service.py (last_login, e le date che ha aggiunto #41), setup.py:561 → utcnow(). - report_service.py:345/:433 generated_at = utcnow(), e nei PDF ogni data si mostra con to_local. - Nomi file production_export_service.py:194 e measurements.py:362: to_local(utcnow()). - Serializzazione: un tipo Annotated UtcDateTime (PlainSerializer che aggiunge tzinfo=UTC ai naive) in src/backend/models/api, usato per TUTTI i campi datetime degli schemi di risposta. - Ingressi: date_from/date_to di statistics.py:35/:103, e ogni altro Query/Body datetime, passano da as_utc_naive. - docker-compose.yml e docker-compose.dev.yml: aggiungere --default-time-zone=+00:00 al command di mysql. UNITÀ B (frontend Flask): - app.py: filtro Jinja local_dt(iso) → stringa "gg/mm/aaaa hh:mm" in Europe/Rome (config DISPLAY_TIMEZONE); usarlo in templates/measure/task_complete.html:186 e in ogni template che taglia stringhe ISO. - statistics/dashboard.html:315-316: date_from = new Date(dateFrom+'T00:00:00').toISOString() (mezzanotte locale convertita in UTC), date_to = fine giornata locale. - JS (recipe_list.html:222, select_recipe.html:262, version_history.html:387, static/js/csv-export.js:213): con il fuso esplicito new Date è già corretto; verificare soltanto e togliere eventuali aggiunte manuali di "Z". TEST A: utcnow naive; una risposta con un datetime ha +00:00; date_from con fuso (+02:00) filtra come il suo equivalente UTC; generated_at del report in ora italiana. TEST B: local_dt("2026-10-06T08:00:00+00:00") → "06/10/2026 10:00"; il test della dashboard, se esiste, controlla i parametri inviati. NON TOCCARE: schema DB (DateTime naive resta); le migrazioni. FATTO SE: uv run pytest verde; grep "datetime.now()\|utcnow()" in src/backend fuori da utils/tempo.py e dai test → vuoto.
Adriano added the assegnata:jeeg label 2026-10-06 09:37:16 +00:00
Author
Owner

⚡ jeeg — preso in carico 2026-10-06T09:37:16Z
ramo: lavoro/jeeg · capacità: docker, gpu, systemd

⚡ jeeg — **preso in carico** 2026-10-06T09:37:16Z ramo: `lavoro/jeeg` · capacità: docker, gpu, systemd
Author
Owner

ESITO: richiesta
COSA: nota all'analisi dopo #41: auth_service.py ha già un utcnow() (commit 35f4692). L'unità A lo sposta in src/backend/utils/tempo.py e lo importa da lì (middleware api_key compreso).

ESITO: richiesta COSA: nota all'analisi dopo #41: auth_service.py ha già un utcnow() (commit 35f4692). L'unità A lo sposta in src/backend/utils/tempo.py e lo importa da lì (middleware api_key compreso).
Author
Owner

Soluzione

ESITO: fatto
RIF: gitea:Adriano/TieMeasureFlow#42
COSA: UTC nel DB e nelle API (ISO +00:00), ora italiana (Europe/Rome) solo in presentazione.
FILE: src/backend/utils/tempo.py — utcnow, as_utc_naive, to_utc_aware, iso_utc, to_local (settings.display_timezone)
FILE: src/backend/models/api/tipi.py — UtcDateTime (serializza +00:00) su tutti gli schemi di risposta; UtcNaiveInput sui 16 parametri Query datetime (aware → UTC naive)
FILE: docker-compose*.yml — MySQL --default-time-zone=+00:00
FILE: src/backend/services/report_service.py — generated_at/measured_at e filtri From/To in ora italiana (_giorno_locale); CSV in UTC ISO
FILE: src/backend/api/routers/setup.py — created_at/last_login via iso_utc
FILE: src/frontend/flask_app/app.py — filtro Jinja local_dt (naive = UTC, None → -), config DISPLAY_TIMEZONE; task_complete.html, dashboard.html (date_from/date_to come istanti UTC del giorno locale), csv-export.js (nome file data locale)
COMMIT: 486e128, 7ac291b, a829bf3 su V3.0.0
PROVA: uv run pytest -q -p no:cacheprovider → 660 passed, 4 skipped
PRESUNTO: nessuna migrazione dati; i container di produzione giravano già in UTC, quindi i dati esistenti sono già UTC naive.
MANCA: vision_worker convert("L") su immagini 16 bit, aperto da #43.

**Soluzione** ESITO: fatto RIF: gitea:Adriano/TieMeasureFlow#42 COSA: UTC nel DB e nelle API (ISO +00:00), ora italiana (Europe/Rome) solo in presentazione. FILE: src/backend/utils/tempo.py — utcnow, as_utc_naive, to_utc_aware, iso_utc, to_local (settings.display_timezone) FILE: src/backend/models/api/tipi.py — UtcDateTime (serializza +00:00) su tutti gli schemi di risposta; UtcNaiveInput sui 16 parametri Query datetime (aware → UTC naive) FILE: docker-compose*.yml — MySQL --default-time-zone=+00:00 FILE: src/backend/services/report_service.py — generated_at/measured_at e filtri From/To in ora italiana (_giorno_locale); CSV in UTC ISO FILE: src/backend/api/routers/setup.py — created_at/last_login via iso_utc FILE: src/frontend/flask_app/app.py — filtro Jinja local_dt (naive = UTC, None → -), config DISPLAY_TIMEZONE; task_complete.html, dashboard.html (date_from/date_to come istanti UTC del giorno locale), csv-export.js (nome file data locale) COMMIT: 486e128, 7ac291b, a829bf3 su V3.0.0 PROVA: uv run pytest -q -p no:cacheprovider → 660 passed, 4 skipped PRESUNTO: nessuna migrazione dati; i container di produzione giravano già in UTC, quindi i dati esistenti sono già UTC naive. MANCA: vision_worker convert("L") su immagini 16 bit, aperto da #43.
Adriano removed the assegnata:jeeg label 2026-10-06 10:02:02 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#42