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.