Jika jadwal mendatang di British Columbia tiba-tiba tampil atau dieksekusi satu jam terlalu cepat atau lambat setelah pembaruan tzdata, jangan langsung menggeser semua nilai di database. Masalah biasanya berasal dari penggunaan offset UTC tetap, singkatan PST/PDT, ketidaksamaan data zona waktu antara aplikasi dan PostgreSQL, atau kesalahan memahami timestamptz.
Langkah pertama dalam debug jadwal PostgreSQL adalah menentukan semantik data: apakah sebuah nilai mewakili instant absolut, atau waktu lokal yang harus tetap sama menurut jam dinding di British Columbia. Perbedaan ini menentukan apakah data perlu dikonversi, dihitung ulang, atau justru tidak boleh diubah.
Perubahan hukum zona waktu dan tanggal penerapannya dapat berkembang. Gunakan basis data zona waktu IANA yang telah dirilis sebagai sumber operasional, lalu verifikasi aturan yang benar-benar terpasang di setiap lingkungan. Artikel Crunchy Data tentang perubahan zona waktu British Columbia memberikan konteks mengenai dampaknya terhadap PostgreSQL dan jadwal masa depan.
Memahami dua jenis waktu yang sering tertukar
Instant absolut
Instant absolut adalah satu titik unik pada garis waktu. Contohnya, transaksi selesai pada 2026-07-15T16:00:00Z. Nilai ini tidak berubah ketika aturan zona waktu berubah; hanya representasi lokalnya yang mungkin berubah.
Di PostgreSQL, tipe yang sesuai adalah timestamp with time zone atau timestamptz. PostgreSQL menyimpan instant tersebut dalam bentuk yang tidak bergantung pada zona sesi. Zona waktu sesi digunakan ketika input ditafsirkan dan hasil ditampilkan.
Intent waktu lokal
Intent waktu lokal menyatakan kehendak seperti “jalankan pukul 09.00 waktu Vancouver setiap hari kerja”. Jika pemerintah mengubah aturan zona waktu, pukul 09.00 harus tetap pukul 09.00 menurut jam lokal, sedangkan instant UTC-nya dapat berubah.
Untuk jadwal semacam ini, simpan setidaknya:
- waktu atau pola lokal, misalnya
09:00atau2026-07-15 09:00:00; - nama zona IANA, yaitu
America/Vancouver; - aturan pengulangan dan kebijakan untuk waktu ambigu atau tidak ada;
next_run_atbertipetimestamptzsebagai hasil perhitungan yang dapat dibangun ulang.
Offset seperti -08:00 tidak cukup untuk menyatakan zona waktu. Offset tidak memuat sejarah maupun aturan masa depan, sehingga tidak dapat menjawab apakah suatu tanggal menggunakan UTC-8, UTC-7, atau aturan baru.
Reproduksi masalah dengan America/Vancouver
Mulailah dengan memeriksa zona sesi PostgreSQL. Pengaturan ini memengaruhi tampilan timestamptz, tetapi tidak mengubah instant yang tersimpan.
SHOW TimeZone;
SET TIME ZONE 'America/Vancouver';
SHOW TimeZone;
Bandingkan konversi berbasis zona regional dengan offset UTC tetap:
SELECT
'2026-07-15 16:00:00+00'::timestamptz AS instant,
'2026-07-15 16:00:00+00'::timestamptz
AT TIME ZONE 'America/Vancouver' AS local_by_region,
timezone(
INTERVAL '-08:00',
'2026-07-15 16:00:00+00'::timestamptz
) AS local_by_fixed_offset;
Jika dua kolom lokal berbeda satu jam, aplikasi yang menggunakan -08:00 sedang mengabaikan aturan regional. Hasil persisnya bergantung pada versi tzdata yang terpasang; karena itu, keluaran contoh ini tidak boleh dijadikan konstanta lintas lingkungan.
Memahami AT TIME ZONE
Operator AT TIME ZONE memiliki dua arah yang berbeda:
timestamp without time zone AT TIME ZONE zonamenafsirkan waktu lokal dalam zona tersebut dan menghasilkantimestamptz.timestamptz AT TIME ZONE zonamenampilkan instant sebagaitimestamp without time zonedi zona tersebut.
WITH input(local_start) AS (
VALUES ('2026-07-15 09:00:00'::timestamp)
)
SELECT
local_start,
local_start AT TIME ZONE 'America/Vancouver' AS absolute_instant,
(local_start AT TIME ZONE 'America/Vancouver')
AT TIME ZONE 'America/Vancouver' AS local_round_trip
FROM input;
Kesalahan umum adalah menganggap timestamptz menyimpan nama zona seperti America/Vancouver. Tipe tersebut menyimpan instant, bukan intent zona asal. Setelah hanya menyimpan timestamptz, database tidak dapat menyimpulkan apakah pengguna sebelumnya memilih Vancouver, Los Angeles, atau offset eksplisit yang kebetulan sama.
Memeriksa zona yang dikenal PostgreSQL
SELECT name, abbrev, utc_offset, is_dst
FROM pg_timezone_names
WHERE name = 'America/Vancouver';
View pg_timezone_names berguna untuk memastikan zona tersedia dan melihat offset yang berlaku pada saat query dijalankan. Nilainya bukan daftar lengkap seluruh transisi masa depan. PostgreSQL juga tidak menyediakan satu cara portabel pada semua instalasi untuk melaporkan nomor versi paket tzdata; catat versi paket sistem, image container, atau artefak build melalui pipeline deployment.
Empat root cause yang paling umum
1. Offset UTC tetap
Kode seperti “Vancouver selalu UTC-8” akan gagal ketika offset legal pada tanggal target berbeda. Perbaikannya adalah memakai America/Vancouver saat mengubah waktu lokal menjadi instant.
2. Singkatan PST atau PDT
Singkatan zona dapat ambigu, bergantung pada konfigurasi, dan tidak membawa aturan transisi masa depan. Jangan menyimpan PST atau PDT sebagai identitas zona untuk jadwal sipil. Gunakan nama IANA dan biarkan tzdata memilih offset yang berlaku pada tanggal target.
3. Tzdata aplikasi dan PostgreSQL berbeda
Aplikasi dapat memakai database zona waktu dari runtime, sistem operasi, ICU, atau JDK, sedangkan PostgreSQL memakai sumber yang ditentukan oleh paket atau build-nya. Akibatnya, aplikasi menghitung 09:00 menjadi satu instant, tetapi PostgreSQL menghitung instant lain.
Bandingkan hasil konversi untuk kumpulan tanggal masa depan yang sama di:
- laptop atau lingkungan pengembangan;
- container aplikasi;
- worker antrean dan scheduler;
- primary dan replica PostgreSQL;
- lingkungan staging dan produksi.
Setelah memperbarui paket zona waktu, ikuti prosedur paket PostgreSQL dan runtime yang digunakan. Recycle proses aplikasi, koneksi lama, dan proses database jika disyaratkan atau jika ada kemungkinan data zona telah di-cache, kemudian verifikasi ulang dengan query, bukan hanya dengan nomor versi paket.
4. Salah memahami efek pembaruan tzdata
Pembaruan tzdata tidak mengubah bit instant yang sudah disimpan dalam kolom timestamptz. Namun, representasi lokal dari instant itu dapat berubah. Sebaliknya, jadwal lokal masa depan mungkin perlu dihitung ulang agar tetap terjadi pada jam dinding yang dimaksud pengguna.
Karena itu, menggeser seluruh kolom timestamptz satu jam biasanya merupakan migrasi yang salah. Transaksi masa lalu dan jadwal yang sejak awal merupakan instant absolut justru akan rusak.
Mendeteksi transisi dan menguji batas waktu
Uji beberapa instant di sekitar tanggal transisi yang relevan. Query berikut menampilkan gerakan jam lokal dalam interval 30 menit. Sesuaikan rentang dengan aturan dalam rilis IANA yang sedang divalidasi.
WITH samples AS (
SELECT generate_series(
'2026-03-08 08:00:00+00'::timestamptz,
'2026-03-08 12:00:00+00'::timestamptz,
INTERVAL '30 minutes'
) AS instant
), converted AS (
SELECT
instant,
instant AT TIME ZONE 'America/Vancouver' AS local_time
FROM samples
)
SELECT
instant,
local_time,
local_time - lag(local_time) OVER (ORDER BY instant) AS local_step
FROM converted
ORDER BY instant;
Jalankan query yang sama pada staging dan produksi. Perbedaan hasil untuk instant yang sama menunjukkan ketidaksamaan aturan zona waktu. Tidak adanya lompatan juga dapat menjadi hasil yang benar apabila aturan terbaru memang menghapus transisi pada rentang tersebut.
Tambahkan pengujian untuk kasus berikut:
- satu menit sebelum, tepat saat, dan satu menit setelah transisi;
- waktu lokal yang tidak ada ketika jam bergerak maju;
- waktu lokal yang muncul dua kali ketika jam bergerak mundur;
- jadwal sekali jalan dan jadwal berulang;
- serialisasi API dengan
Z, offset numerik, dan nama zona terpisah; - perhitungan ulang
next_run_atsetelah versi tzdata berubah.
Jangan bergantung pada resolusi implisit untuk waktu lokal ambigu atau tidak ada. Tetapkan kebijakan bisnis, misalnya menolak input, memilih kemunculan pertama atau kedua, atau memindahkan eksekusi ke waktu valid berikutnya. Simpan pilihan tersebut agar perhitungan ulang menghasilkan keputusan yang sama.
Memperbaiki data tanpa menggeser record yang sudah benar
Sebelum migrasi, klasifikasikan record berdasarkan semantiknya. Jika tabel hanya memiliki timestamptz tanpa waktu lokal, zona IANA, atau provenance input, intent asli mungkin tidak dapat direkonstruksi secara aman.
Model jadwal yang lebih eksplisit dapat berbentuk seperti berikut:
CREATE TABLE schedules (
id bigint PRIMARY KEY,
local_start timestamp without time zone,
zone_name text,
time_semantics text,
next_run_at timestamptz,
status text
);
Dalam sistem nyata, tambahkan constraint atau enum sesuai standar proyek. time_semantics dapat membedakan jadwal berbasis jam lokal dari event absolut.
Lakukan dry run sebelum UPDATE
WITH candidates AS (
SELECT
id,
next_run_at AS old_instant,
local_start AT TIME ZONE zone_name AS expected_instant
FROM schedules
WHERE zone_name = 'America/Vancouver'
AND time_semantics = 'wall_clock'
AND status = 'scheduled'
AND next_run_at > now()
)
SELECT
id,
old_instant,
expected_instant,
expected_instant - old_instant AS difference
FROM candidates
WHERE old_instant IS DISTINCT FROM expected_instant
ORDER BY id;
Tinjau jumlah record, distribusi selisih, tanggal target, dan sumber pembuat jadwal. Selisih selain pola yang diperkirakan dapat menunjukkan bug lain, data manual, atau zona yang sebelumnya salah.
Perbarui hanya record yang terbukti terdampak
WITH candidates AS (
SELECT
id,
local_start AT TIME ZONE zone_name AS expected_instant
FROM schedules
WHERE zone_name = 'America/Vancouver'
AND time_semantics = 'wall_clock'
AND status = 'scheduled'
AND next_run_at > now()
), changed AS (
SELECT c.id, c.expected_instant
FROM candidates c
JOIN schedules s USING (id)
WHERE s.next_run_at IS DISTINCT FROM c.expected_instant
)
UPDATE schedules s
SET next_run_at = c.expected_instant
FROM changed c
WHERE s.id = c.id
RETURNING s.id, s.next_run_at;
Filter tambahan berdasarkan batch pembuat, versi aturan, tenant, atau daftar ID hasil audit akan lebih aman daripada mengandalkan selisih satu jam saja. Simpan snapshot nilai lama dan hasil RETURNING dalam tabel audit sebelum commit. Jangan menyentuh transaksi masa lalu, event absolut, atau jadwal yang tidak memiliki bukti intent lokal.
Strategi rollout, observability, dan rollback
- Inventarisasi. Temukan penggunaan
PST,PDT,-08:00, konversi manual, cron, worker, cache jadwal, dan layanan eksternal. - Samakan tzdata. Bangun image aplikasi dan database dengan rilis yang disetujui. Catat digest image serta versi paket dalam metadata deployment.
- Validasi staging. Jalankan query konversi dan test transisi dari produksi terhadap staging tanpa menulis data.
- Deploy kode lebih dahulu. Pastikan jadwal baru menyimpan intent lokal dan zona IANA, serta tidak lagi memakai offset tetap.
- Migrasikan secara bertahap. Proses hanya jadwal masa depan yang terklasifikasi, gunakan batch kecil, dan buat migrasi idempoten.
- Bangun ulang cache. Invalidasi heap scheduler, antrean tertunda, atau cache
next_run_atyang dibuat dengan aturan lama. - Verifikasi. Bandingkan waktu lokal yang diharapkan dengan waktu eksekusi aktual untuk zona Vancouver.
Metrik dan log yang perlu tersedia
schedule_id, nama zona, waktu lokal, dan instant UTC yang dihitung;- offset yang dipakai pada perhitungan;
- versi atau identitas artefak tzdata aplikasi dan database;
- selisih antara
scheduled_fordan waktu worker mulai mengeksekusi; - jumlah jadwal yang dihitung ulang serta distribusi selisihnya;
- alarm untuk lonjakan eksekusi terlalu awal, terlambat, ganda, atau terlewat.
Untuk rollback, simpan nilai next_run_at lama per record dan versi kode yang menghasilkan nilai baru. Rollback data harus menggunakan audit tersebut, bukan sekadar mengurangi atau menambah satu jam. Mengembalikan paket tzdata lama secara global juga berisiko memengaruhi zona lain, sehingga sebaiknya dilakukan hanya sebagai rollback deployment terkontrol dan disertai verifikasi.
Checklist diagnosis singkat
- Pastikan nilai tersebut merupakan instant absolut atau intent waktu lokal.
- Jalankan
SHOW TimeZonedan periksapg_timezone_names. - Bandingkan
America/Vancouverdengan offset tetap menggunakanAT TIME ZONE. - Bandingkan hasil aplikasi, worker, PostgreSQL primary, dan replica.
- Jangan gunakan PST/PDT sebagai identitas zona jadwal.
- Jangan menggeser semua
timestamptzsatu jam. - Hitung ulang hanya jadwal lokal masa depan yang terbukti terdampak.
- Uji transisi, lakukan rollout bertahap, dan siapkan audit untuk rollback.
Prinsip utamanya sederhana: simpan instant untuk kejadian absolut, tetapi simpan waktu lokal beserta America/Vancouver untuk intent sipil. Dengan pemisahan ini, pembaruan aturan zona waktu dapat ditangani melalui perhitungan ulang jadwal masa depan tanpa merusak record yang sudah benar.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!