Che formato per i timestamp in JSON
In sintesi: I numeri epoch e le stringhe ISO hanno ciascuno i propri compromessi, ma è mescolarli ciò che davvero rompe le cose. Usa ISO 8601 con un offset, memorizza in UTC, renderizza nel fuso orario dell'utente.

In sintesi: usa ISO 8601 con un offset esplicito sul filo, memorizza UTC con una Z finale, e converti solo al momento della visualizzazione; se scegli invece numeri epoch, fissa globalmente secondi versus millisecondi e metti l'unità nel nome del campo.
Perché 2026-10-30 14:30:00 non basta
Quella stringa non porta alcun fuso. Le 14:30 a Shanghai e le 14:30 a Londra sono a otto ore di distanza, e non c'è modo di sapere quale si intenda. new Date('2026-10-30 14:30:00') analizza nel fuso locale del runtime, quindi il server e il browser ricavano due istanti diversi dallo stesso payload — il tipo di bug che non si riproduce mai.
Una 2026-10-30 nuda non è affatto un istante. Va bene per compleanni e periodi di fatturazione, sbagliata per un orario di creazione.
Due formati praticabili
Stringhe ISO 8601
{
"createdAt": "2026-10-30T14:30:00+08:00",
"updatedAt": "2026-10-30T06:30:00Z"
}
Leggibili, ordinabili quando il formato è coerente, con precisione al millisecondo se necessario, e autodescrittive.
Numeri epoch
{ "createdAt": 1793519400 }
Compatti e non ambigui, ma illeggibili, e secondi versus millisecondi è un pericolo permanente. Se scegli gli epoch, standardizza l'unità e mettila nel nome del campo: createdAtMs è molto più sicuro di createdAt.
Convenzioni da fissare presto
| Voce | Convenzione |
|---|---|
| Formato sul filo | ISO 8601 con Z o +08:00 |
| Fuso di archiviazione | UTC, così l'ora legale non entra mai in gioco |
| Precisione | Il millisecondo basta |
| Denominazione dei campi | createdAt, updatedAt, expiresAt |
| Date pure | Un campo date separato, per esempio birthday: "1990-01-01" |
| Durate | Intero più un'unità, per esempio timeoutMs: 3000 |
Rendering
new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Europe/London',
}).format(new Date('2026-10-30T06:30:00Z'));
Specifica timeZone esplicitamente quando la visualizzazione non deve seguire il dispositivo. Affidarsi al default del browser è corretto per gli orari mostrati all'utente e sbagliato per i report.
Tre bug ricorrenti
Le date nude si analizzano come mezzanotte UTC. Secondo la specifica ECMAScript, le stringhe solo-data sono UTC mentre le stringhe data-ora sono locali. Questa incoerenza è una fonte affidabile di bug di un giorno in più o in meno.
Il fuso del database non concorda con il serializzatore. Il datetime di MySQL non ha fuso; scrivi locale, leggi come UTC, e tutto si sposta dell'offset.
Ora legale. Memorizzare orari locali futuri crea istanti che non esistono o che si verificano due volte. UTC non ha questi buchi.
FAQ
Come devo scegliere tra ISO 8601 e numeri epoch?
Usa ISO 8601 quando le persone leggono il payload, quando vuoi un ordine lessicografico o quando il valore deve portare il proprio fuso; il costo è circa una dozzina di byte in più e una libreria di parsing. Usa i numeri epoch quando compattezza e gestione banale tra linguaggi contano di più, ma fissa globalmente secondi versus millisecondi e indica quale unità contiene il campo.
Una data nuda e un timestamp devono condividere lo stesso campo?
No. Una 2026-10-30 nuda è un giorno di calendario senza ora e senza fuso, mentre un campo timestamp ci si aspetta che porti un istante esatto. Mantieni compleanni e periodi di fatturazione in un campo date separato, e usa un campo ISO 8601 con suffisso At per gli orari di creazione o aggiornamento.