← Tutti gli articoli

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

What is the most common Unix timestamp bug?
Passing seconds where milliseconds are expected, or the reverse. In JavaScript, new Date(unixSeconds) lands in 1970 because the constructor expects milliseconds — multiply the 10-digit value by 1000 first.
How do I prevent seconds-vs-milliseconds bugs?
Name fields with their unit (createdAtMs, expiresAtSeconds), convert at system boundaries, and prefer ISO 8601 strings in API contracts so the value is self-describing.
Why does adding 86,400,000 ms break 'tomorrow'?
Local calendar days are 23 or 25 hours long during daylight saving transitions, so adding a fixed 24 hours can land on the wrong date. Use calendar arithmetic in the target timezone instead.