Zona Waktu Adalah Palsu (Dan Cara Mengelolanya Dalam Kode)
Zona waktu terlihat seperti pergeseran UTC yang sederhana. Tapi sebenarnya tidak. Panduan ini menjelaskan mengapa zona waktu mengganggu kode — celah DST, pergeseran setengah jam, waktu tanpa kesadaran — dan cara mengelolanya secara benar dalam JavaScript, Python, PHP, dan SQL.
Anda menanyakan waktu kepada server Anda. Server tersebut mengatakan pukul 14:00. Anda menyimpannya. Anda meminta kembali. Sekarang ia mengatakan pukul 16:00. Anda tidak mengubah apa pun. Selamat datang di zona waktu.
Zona waktu adalah salah satu masalah paling mengejutkan dalam perangkat lunak. Secara permukaan, mereka terlihat seperti perbedaan waktu UTC — cukup tambahkan atau kurangi beberapa jam. Namun di bawah lapisan, mereka adalah campuran acak dari keputusan politik, kejadian historis, perbedaan waktu setengah jam, dan aturan penghematan waktu yang berubah tanpa peringatan. Artikel ini menjelaskan mengapa zona waktu begitu menyulitkan dan, lebih penting lagi, bagaimana cara mengelolanya secara benar dalam kode.
Mengapa “Hanya Gunakan UTC” Hanya Setengah Jawabannya
Saran paling umum yang Anda dengar adalah: simpan semua data dalam UTC. Saran ini benar — tetapi tidak lengkap. Simpan dalam UTC menyelesaikan masalah (penyimpanan konsisten) sambil meninggalkan masalah lain yang lebih besar: tampilan dan input. satu masalah
Seorang pengguna di Tokyo menjadwalkan pertemuan pukul 9 pagi waktu mereka. Anda menyimpannya dalam bentuk UTC. Kemudian, seorang pengguna di New York membuka acara yang sama. Anda menampilkan acaranya dalam waktu lokal mereka. Namun, waktu lokal mana yang berlaku ketika pengguna bepergian? Saat mereka memperbarui jam sistem mereka? Saat penghematan waktu dimulai? “Simpan dalam UTC, tampilkan dalam waktu lokal” adalah kebijakan yang baik — tetapi eksekusi yang menghancurkan kebanyakan tim.
Masalah Nyata Dengan Zona Waktu
Sebelum kita sampai pada solusi, membantu untuk memberi nama apa yang sebenarnya kita hadapi.
1. Offset UTC Tidak Adalah Zona Waktu
UTC+5:30 Bukan “waktu India.” Ini adalah offset statis. Waktu Standar India adalah Asia/Kolkata — zona waktu yang bernama dan menggunakan +5:30 serta tidak pernah mengamati penghematan waktu. Ini adalah dua hal yang berbeda. Jika Anda mengatur offset secara manual, Anda menyimpan angka. Jika Anda menyimpan zona bernama, Anda menyimpan niat.
Zona bernama hidup di dalam Database Waktu IANA (juga disebut tzdata atau database Olson). Setiap bahasa dan sistem operasi yang serius membawa salinan dari database tersebut. Gunakan itu. Selalu pilih America/New_York daripada UTC-5.
2. Penghematan Waktu Menggerakkan Jam (Tidak Dapat Diprediksi)
Transisi DST menciptakan dua kasus tepat yang berbahaya:
- Kekosongan “maju musim semi”: Jam melompat dari pukul 2:00 ke pukul 3:00. Waktu pukul 2:30 secara harfiah tidak ada. Jika Anda mencoba menjadwalkan sesuatu pada pukul 2:30 pada hari itu, Anda akan mendapatkan kesalahan atau perilaku yang diam-diam salah tergantung pada library Anda.
- Kebingungan “mengembalikan waktu”: Jam bergerak dari pukul 2:00 kembali ke pukul 1:00. Waktu pukul 1:30 kini terjadi dua kali. Tanpa konteks tambahan (flag fold), Anda tidak bisa mengetahui kapan Anda berarti.
Dan aturan DST berubah. Negara dan negara bagian di AS telah beralih, menghentikan, atau mengubah aturan mereka dalam memori. Perilaku kode Anda tergantung pada keberadaan paket tzdata yang terbaru — ini adalah masalah operasional, bukan hanya masalah pengembang.
3. Tidak Semua Offset Adalah Jam Penuh
India adalah UTC+5:30. Nepal adalah UTC+5:45. Bagian dari Australia adalah UTC+9:30. Iran adalah UTC+3:30 di musim dingin dan UTC+4:30 di musim panas. Jika sistem Anda mengasumsikan zona waktu selalu pada jam penuh, maka akan secara diam-diam merusak waktu untuk jutaan pengguna.
4. “Waktu Lokal” di Server Tidak Bermakna
Server memiliki jam sistem. Jam-jam tersebut memiliki zona waktu yang dikonfigurasi — sering kali UTC, kadang-kadang yang diatur oleh penyedia hosting secara default, atau yang diatur oleh administrator sistem bertahun-tahun lalu. Kode yang memanggil new Date() atau datetime.now() tanpa menyertakan zona secara eksplisit mengandalkan konfigurasi server tersebut secara implisit. Lingkungan yang berbeda akan menghasilkan hasil yang berbeda. Ini adalah bug yang menunggu terjadinya pada setiap penyebaran.
Pendekatan yang Benar, Berdasarkan Bahasa
JavaScript / TypeScript
Objek native dalam JavaScript adalah penutup tipis dari timestamp milidetik UTC. Ini terlihat ramah; namun tidak. Hindari memformat secara manual — gunakan API Date untuk tampilan, atau gunakan library. Intl.DateTimeFormat Standar modern adalah Temporal
— proposal TC39 yang telah diimplementasikan di Chrome 121 dan akan datang ke semua runtime utama. Ini memiliki dukungan pertama kelas untuk zona waktu dan merupakan jawaban yang benar jangka panjang: Jika Anda belum bisa menggunakan Temporal,
// Store an instant (UTC-equivalent)
const meeting = Temporal.Instant.from("2025-06-15T14:00:00Z");
// Display in a specific zone
const nyTime = meeting.toZonedDateTimeISO("America/New_York");
console.log(nyTime.toString()); // 2025-06-15T10:00:00-04:00[America/New_York]
// Convert to Tokyo time
const tokyoTime = meeting.toZonedDateTimeISO("Asia/Tokyo");
console.log(tokyoTime.toString()); // 2025-06-16T23:00:00+09:00[Asia/Tokyo]
date-fns-tz dengan date-fns adalah pilihan yang dapat diandalkan. Luxon adalah pilihan lain yang kuat. Moment.js sudah lengkap fitur tetapi tidak lagi dijaga — pindahlah dari itu. Modul Python
Ular piton
membedakan antara “datetime tidak sadar” (tidak memiliki informasi zona waktu) dan “datetime sadar” (dengan tzinfo). Datetime tidak sadar adalah penipuan — mereka terlihat valid tetapi tidak memiliki makna di antara sistem. datetime Gunakan selalu datetime sadar. Gunakan modul
untuk dukungan zona waktu IANA: zoneinfo Untuk Python 3.8 dan sebelumnya, gunakan library — tetapi hati-hati dengan API localize/normalize dari
from datetime import datetime
from zoneinfo import ZoneInfo
# Aware datetime — always do this
utc_time = datetime(2025, 6, 15, 14, 0, 0, tzinfo=ZoneInfo("UTC"))
# Convert to New York time
ny_time = utc_time.astimezone(ZoneInfo("America/New_York"))
print(ny_time) # 2025-06-15 10:00:00-04:00
# Never do this — naive datetime, meaningless
bad = datetime(2025, 6, 15, 14, 0, 0) # what zone is this?
yang memiliki celah yang pytz hindari. pytzKelas-kelas ini mendukung zona IANA secara natif melalui zoneinfo . Prefer
PHP
PHP’s DateTime dan DateTimeImmutable — lebih aman karena perubahan menghasilkan objek baru daripada mengubah secara langsung. DateTimeZoneDatabase / SQL DateTimeImmutable Pengelolaan zona waktu di database adalah medan yang penuh bahaya sendiri.
$utc = new DateTimeImmutable('2025-06-15T14:00:00', new DateTimeZone('UTC'));
// Convert to Sydney time
$sydney = $utc->setTimezone(new DateTimeZone('Australia/Sydney'));
echo $sydney->format('Y-m-d H:i:s T'); // 2025-06-16 00:00:00 AEST
// Store timestamps as ISO 8601 strings or Unix timestamps
echo $utc->getTimestamp(); // 1749996000
PostgreSQL:
(timestamp with time zone) — menyimpan semua data dalam bentuk UTC dan mengonversinya saat keluar. Jangan gunakan
- (tanpa zona waktu) untuk data yang ditampilkan kepada pengguna; ini menyimpan apa pun yang Anda berikan tanpa konversi. Uji dengan validator:
TIMESTAMPTZMySQL:TIMESTAMPjenis adalah tidak sadar (tidak memiliki zona). Gunakan - jika Anda ingin penyimpanan dalam UTC, tetapi perhatikan rentangnya terbatas hingga tahun 2038. Untuk skema baru, penyimpanan dalam bentuk Itu
DATETIMEdalam format ISO 8601 atau sebagai timestamp Unix dalamTIMESTAMPsering kali lebih aman.VARCHARSQLite:BIGINTTidak memiliki tipe waktu zona secara natif. Simpan dalam bentuk teks ISO 8601 ( - ) atau sebagai bilangan Unix. Atur konversi di kode aplikasi. Tidak peduli database mana yang Anda gunakan, atur zona waktu sesi server secara eksplisit, bukan mengandalkan default.
2025-06-15T14:00:00ZAturan Praktis yang Bisa Diterapkan
Setelah memahami teori, berikut adalah aturan konkret yang mencegah kebanyakan bug zona waktu:
Simpan UTC di mana saja.
Setiap timestamp di database harus dalam bentuk UTC. Tidak ada pengecualian untuk “hanya digunakan secara internal.”
- Gunakan nama zona IANA, bukan offset. Simpan
- bersamaan dengan timestamp UTC saat Anda perlu membangun kembali waktu lokal awal. Offset sendiri tidak dapat dipulihkan setelah transisi DST. Jangan panggil “sekarang” tanpa zona.
America/Chicago— selalu berikan argumen zona secara eksplisit atau segera tambahkan satu. - Konversi ke waktu lokal pada saat terakhir.
datetime.now(),new Date(),time()Lakukan semua perhitungan tanggal dalam UTC. Konversi ke zona waktu lokal hanya dilakukan sebelum tampilan. - Jaga tzdata tetap terbaru. Aturan zona waktu berubah. Jadikan pembaruan tzdata bagian dari manajemen ketergantungan rutin Anda.
- Uji di sekitar transisi DST. Dua tanggal berbahaya setiap tahun (maju musim semi, kembali ke waktu) harus dimasukkan ke dalam suite uji jika Anda menangani jadwal atau data yang sensitif terhadap waktu.
- Ajukan kepada pengguna zona waktu mereka secara eksplisit. Jangan mengandalkan geolokasi IP atau tebakan browser untuk hal-hal penting. Tampilkan picker zona waktu; simpan hasilnya.
- Catatan tentang Unix Timestamp Unix timestamp — detik sejak 1970-01-01T00:00:00Z — bersifat bebas zona waktu secara definisi. Ini adalah format penyimpanan yang valid dan sangat berguna dalam log, API, dan cache di mana Anda ingin angka tunggal yang tidak ambigu.
Kecuali: Unix timestamp tidak membawa niat pengguna
asli
. Jika seseorang memesan penerbangan untuk “pagi hari waktu London”, dan Anda hanya menyimpan timestamp Unix, Anda kehilangan fakta bahwa mereka berarti London. Saat penerbangan direncanakan kembali tiga bulan kemudian dan London berpindah masuk atau keluar dari BST, Anda mungkin menampilkan waktu lokal yang salah. Simpan zona waktu bersama dengan timestamp saat niat pengguna penting. Kasus Paling Sulit: Acara yang BerulangAcara yang berulang — pertemuan mingguan, siklus pembayaran bulanan, pengingat harian — mengungkapkan semua kasus tepi zona waktu sekaligus.
Pertimbangkan aturan “setiap Senin pukul 9 pagi” untuk pengguna di Los Angeles. Apakah Anda menyimpan pukul 9 pagi waktu Pacific? Apa yang terjadi ketika mereka bepergian ke Tokyo? Apa yang terjadi ketika jam maju musim semi dan hari Senin jatuh pada tanggal transisi?
Model yang benar adalah:
Simpan aturan ulang sebagai waktu dinding + nama zona IANA: “Senin 09:00 America/Los_Angeles”
Hitung kejadian UTC pada saat penjadwalan, bukan saat pembuatan aturan
- Hitung ulang setelah transisi DST yang terjadi dalam jangkauan ulang
- Library seperti
- (JS) dan
(Python) menangani ini dengan benar ketika diberi konteks zona waktu yang tepat. Membuat logika ini secara manual adalah jalan yang dapat menghasilkan bug halus yang muncul bulan kemudian. rrule.js Zona Waktu Adalah Kebalikan (Dan Cara Mengelolanya dalam Kode) 2 dateutil.rrule Zona Waktu Adalah Kebalikan (Dan Cara Mengelolanya dalam Kode) 1
Instal Ekstensi Kami
Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat
恵 Papan Skor Telah Tiba!
Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!
Alat Wajib Coba
Lihat semua Pendatang baru
Lihat semuaMemperbarui: Kita alat terbaru ditambahkan pada 27 Apr 2026
