← semua materi
19:00 Tips · 23 Oct 2026 · 19:00

Bug timezone & DST yang memanggang produksi


Isi

Timezone bug bukan bug logika. Bug ini bug asumsi. Kode benar, tes hijau, lalu Senin pagi laporan masuk: "data tanggal 27 hilang", "job jalan dua kali", "invoice kemarin masuk ke hari ini".

Tiga kelas bug yang paling sering menghantam produksi:

1. Nyimpen waktu lokal tanpa offset

-- salah
created_at DATETIME  -- "2026-10-23 14:00" — 14:00 di mana?

String itu tidak bisa diurutkan lintas region, tidak bisa dikonversi, dan jadi ambigu dua kali setahun saat DST fall-back (jam 01:30 terjadi dua kali).

Aturan main:

Pengecualian penting: waktu yang "bermakna lokal" — jadwal meeting, reminder harian jam 9 pagi, jam buka toko. Itu bukan instant, itu intent. Simpan UTC plus nama timezone IANA (Asia/Jakarta), bukan offset +07:00. Offset bisa berubah (kebijakan pemerintah), nama timezone mengikuti database tz.

2. Asumsi "1 hari = 24 jam"

Di zona ber-DST, satu hari bisa 23 atau 25 jam.

# rapuh
besok = sekarang + timedelta(days=1)

# benar kalau maksudnya "tanggal besok, jam sama"
besok = (sekarang.astimezone(tz) + timedelta(days=1)).replace(hour=9)

Konsekuensi nyata: laporan "penjualan 24 jam terakhir" bolong 1 jam atau dobel 1 jam saat transisi. Billing cycle yang dihitung +30*24*3600 detik bisa geser satu jam dan bikin tagihan beda tipis — cukup buat tim finance buka tiket.

3. Cron job di jam yang tidak ada

Spring forward: jam 02:00–02:59 tidak pernah terjadi. Job 30 2 * * * dilewati. Fall back: jam 01:00–01:59 terjadi dua kali. Job jalan dua kali.

Perilaku cron beda-beda antar implementasi (Vixie cron, systemd timer, Kubernetes CronJob, cloud scheduler) — sebagian skip, sebagian jalan sekali di waktu pertama, sebagian dua kali. Jangan diasumsikan. Dites.

Pola aman: jangan jadwalkan job kritis di 00:00–03:00 lokal. Pilih 04:00. Atau jadwalkan dalam UTC dan buat job-nya idempoten (kunci berbasis tanggal bisnis, bukan timestamp eksekusi).

Kasus dunia nyata yang bikin geleng kepala

Bau-bau kode yang harus dicurigai


Satu hal yang bisa langsung dipakai

Audit cepat, 5 menit, di repo mana pun:

# cari kandidat bahaya
rg -n "datetime\.now\(\)|new Date\(\)|getTimezoneOffset|toISOString\(\)|\+07:00|3600 \* 24|86400"

Lalu cek satu pertanyaan untuk tiap hasil: "nilai ini instant atau wall clock?" Kalau instant → harus UTC. Kalau wall clock → harus bawa nama timezone.

Bonus tes murah: jalankan test suite dengan TZ=America/Santiago dan TZ=Asia/Jakarta. Banyak bug timezone langsung muncul tanpa perlu nulis tes baru. Satu baris, hasil instan.


Takeaway

UTC untuk menyimpan, timezone name untuk bermakna, dan jangan pernah percaya bahwa satu hari itu 24 jam.

Kalau cuma ingat satu hal: offset adalah fakta yang bisa berubah oleh keputusan politik — nama timezone adalah satu-satunya yang tahan.