7 bug di timestamp Unix che ogni sviluppatore ha rilasciato
Questi sono gli errori di timestamp che causano incidenti in produzione: discrepanze silenziose di unità, ipotesi sui fusi, il ×1000 dimenticato, l'archiviazione come stringa, il parsing ambiguo e gli errori ai confini dell'ora legale. Ogni bug include il sintomo, la causa radice e una correzione pratica.
Bug 1: manca × 1000 in JavaScript
new Date(1700000000) produce una data vicina a gennaio 1970, non a novembre 2023. Il costruttore Date di JavaScript si aspetta millisecondi, ma la maggior parte delle API server e dei database restituisce secondi Unix. Questo bug compare spesso prima nelle dashboard di amministrazione perché il payload del backend è JSON valido e la UI mostra semplicemente l'anno sbagliato.
- Wrong: new Date(response.created_at) — if created_at is Unix seconds
- Right: new Date(response.created_at * 1000)
- Detection: if your date shows a year near 1970, the timestamp is in seconds not milliseconds
- Safe wrapper: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)
Bug 2: unità miste al confine di un sistema
JavaScript restituisce millisecondi. Python, Go e la maggior parte delle API lato server restituiscono secondi. Quando JavaScript invia un timestamp a un backend Python senza convertirlo, il backend interpreta un valore in millisecondi come secondi, collocando l'evento nell'anno 55792. Il senso inverso è altrettanto grave: la UI riceve secondi, li passa direttamente a new Date() e mostra gennaio 1970.
- JavaScript sends: Date.now() → 1700000000000 (ms)
- Python receives: datetime.fromtimestamp(1700000000000) → year 55792
- Fix: divide by 1000 before sending to a non-JavaScript system
- Better fix: use ISO 8601 strings at API boundaries — they are unambiguous
Bug 3: affidarsi al fuso locale del server
Il codice che usa datetime.fromtimestamp() di Python senza specificare UTC produce risultati diversi a seconda del fuso configurato del server. Funziona in sviluppo e si rompe in produzione quando si fa il deploy in un'altra regione. La stessa categoria di bug compare in Node quando il codice formatta date senza un'opzione timeZone esplicita e poi congela il risultato nei test.
- Wrong: datetime.fromtimestamp(ts) — uses server's local timezone
- Right: datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
- Wrong in JS: date.toLocaleDateString() — uses the Node process timezone
- Right in JS: date.toISOString() or Intl.DateTimeFormat with an explicit timeZone option
Bug 4: memorizzare i timestamp come stringhe
Memorizzare un timestamp come stringa formattata in una colonna VARCHAR sembra innocuo finché non serve ordinare per tempo, interrogare un intervallo o fare aritmetica. Il confronto tra stringhe di date funziona solo con campi ISO 8601 rigorosi e con zeri di riempimento. Stringhe leggibili come Nov 15, 2023 o 1/2/24 sono formati di visualizzazione, non di archiviazione.
- Wrong: INSERT INTO events (created) VALUES ('Nov 15, 2023') — not sortable
- Wrong: INSERT INTO events (created) VALUES ('2023-11-15') — loses time component
- Right: use a native DATETIME/TIMESTAMPTZ column or BIGINT for Unix milliseconds
- If a string is unavoidable: use ISO 8601 with full precision: '2023-11-15T06:13:20Z'
Bug 5: parsare stringhe di data ambigue
new Date('2024-01-01') e new Date('2024/01/01') sembrano equivalenti ma si comportano diversamente. Il formato ISO 8601 con trattini viene parsato come mezzanotte UTC; il formato con barre dipende dall'implementazione e la maggior parte dei browser lo parsa come mezzanotte locale. Un selettore di data, un payload API e una riga di database possono mostrare la stessa data di calendario rappresentando istanti diversi.
- new Date('2024-01-01') → January 1, 2024 00:00:00 UTC (correct, explicit)
- new Date('2024/01/01') → January 1, 2024 00:00:00 local time (varies by runtime)
- new Date('January 1, 2024') → local time, may be rejected by strict parsers
- Safe rule: always use ISO 8601 with explicit timezone: new Date('2024-01-01T00:00:00Z')
Bug 6: sommare 24 ore per ottenere domani
Sommare 86.400.000 millisecondi sembra un modo pulito per ottenere domani, ma i giorni di calendario locali non durano sempre 24 ore. Durante le transizioni dell'ora legale, un giorno locale può essere di 23 o 25 ore. Se la logica del prodotto significa il prossimo giorno di calendario locale, usa l'aritmetica di calendario nel fuso di destinazione, non l'aritmetica di durata in millisecondi UTC.
- Sbagliato per i giorni di calendario: next = new Date(date.getTime() + 86400000)
- Meglio nel codice Date locale: copia il Date e poi chiama setDate(d.getDate() + 1)
- Per il codice server: usa una libreria che gestisce i fusi o l'aritmetica di date del database
- Per le pianificazioni ricorrenti: memorizza local_date, local_time e timezone_id, poi calcola ogni occorrenza
- Aggiungi test per le date di inizio e fine dell'ora legale nel fuso di destinazione
Bug 7: filtri di fine giornata inclusivi
Una query di analytics comune usa created_at >= start e created_at <= endOfDay. Sembra ragionevole, ma crea bug di precisione quando il database memorizza millisecondi, microsecondi o nanosecondi. Il pattern più sicuro è un intervallo semiaperto: maggiore o uguale all'inizio e strettamente minore dell'inizio del periodo successivo.
- Risky: WHERE created_at <= '2026-05-19 23:59:59'
- Safer: WHERE created_at >= '2026-05-19' AND created_at < '2026-05-20'
- Half-open ranges work for seconds, milliseconds, microseconds, and nanoseconds
- The same pattern works for months: created_at >= monthStart AND created_at < nextMonthStart
Checklist di prevenzione in produzione
La maggior parte degli incidenti con i timestamp è evitabile con denominazione, test e confini espliciti. L'obiettivo non è rendere ingegnosa ogni riga di codice di date; è rendere unità, fuso e semantica di intervallo noiosamente ovvi.
- Dai ai campi numerici un nome con l'unità: createdAtMs, expiresAtSeconds, imported_at_ms
- Usa UTC per l'archiviazione e fusi IANA espliciti per la visualizzazione
- Preferisci stringhe ISO 8601 con Z o offset ai confini API ispezionati da persone
- Usa intervalli semiaperti per i filtri di data
- Testa intorno al 1° gennaio, ai giorni bisestili, alle transizioni dell'ora legale e al limite dell'anno 2038
FAQ sui bug dei timestamp Unix
- Qual è il bug di timestamp Unix più comune?
- Passare secondi dove ci si aspettano millisecondi, o viceversa. In JavaScript, new Date(unixSeconds) cade nel 1970 perché il costruttore si aspetta millisecondi; moltiplica prima il valore a 10 cifre per 1000.
- Come prevengo i bug secondi-vs-millisecondi?
- Dai ai campi un nome con l'unità (createdAtMs, expiresAtSeconds), converti ai confini del sistema e preferisci stringhe ISO 8601 nei contratti API così che il valore si autodescriva.
- Perché sommare 86.400.000 ms rompe «domani»?
- I giorni di calendario locali durano 23 o 25 ore durante le transizioni dell'ora legale, quindi sommare 24 ore fisse può cadere sulla data sbagliata. Usa l'aritmetica di calendario nel fuso di destinazione.