Hal-hal penting yang harus dipahami
timestamp without time zone AT TIME ZONE
menafsirkan ulang sebuah timestamp
berada di zona waktu itu untuk tujuan mengubahnya menjadi UTC .
timestamp with time zone AT TIME ZONE
mengkonversi sebuah timestamptz
menjadi timestamp
pada zona waktu yang ditentukan.
PostgreSQL menggunakan zona waktu ISO-8601, yang menentukan bahwa timur Greenwich adalah positif ... kecuali jika Anda menggunakan penentu zona waktu POSIX, dalam hal ini mengikuti POSIX. Kegilaan pun terjadi.
Mengapa yang pertama menghasilkan hasil yang tidak terduga
Stempel waktu dan zona waktu dalam SQL mengerikan. Ini:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
menginterpretasikan literal yang tidak diketahui jenisnya '2011-12-30 00:30:00'
sebagai timestamp without time zone
, yang diasumsikan Pg berada di TimeZone lokal kecuali jika diberitahu sebaliknya. Saat Anda menggunakan AT TIME ZONE
, itu (sesuai spesifikasi) diinterpretasikan ulang sebagai timestamp with time zone
di zona waktu EST5EDT
kemudian disimpan sebagai waktu absolut dalam UTC - jadi diubah dari EST5EDT
ke UTC, yaitu offset zona waktu dikurangi . x - (-5)
adalah x + 5
.
Stempel waktu ini, disesuaikan dengan penyimpanan UTC, kemudian disesuaikan untuk server Anda TimeZone
pengaturan untuk tampilan sehingga ditampilkan dalam waktu lokal.
Jika Anda ingin mengatakan "Saya memiliki stempel waktu ini dalam waktu UTC, dan ingin melihat waktu lokal yang setara dalam EST5EDT", jika Anda ingin independen dari pengaturan TimeZone server, Anda perlu menulis sesuatu seperti:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Ini mengatakan "Stempel waktu yang diberikan 2011-12-30 00:30:00, perlakukan itu sebagai stempel waktu di UTC saat mengonversi ke stempel waktu, lalu ubah stempel waktu itu ke waktu lokal di EST5EDT".
Mengerikan, bukan? Saya ingin memberi perusahaan berbicara siapa pun yang memutuskan semantik gila AT TIME ZONE
- seharusnya benar-benar seperti timestamp CONVERT FROM TIME ZONE '-5'
dan timestamptz CONVERT TO TIME ZONE '+5'
. Juga, timestamp with time zone
harus benar-benar membawa zona waktunya, tidak disimpan dalam UTC dan dikonversi secara otomatis ke waktu lokal.
Mengapa yang kedua berhasil (selama TimeZone =UTC)
Versi "berfungsi" asli Anda:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
hanya akan benar jika TimeZone disetel ke UTC, karena pemeran text-to-timestamptz mengasumsikan TimeZone saat tidak ditentukan.
Mengapa yang ketiga berhasil
Dua masalah membatalkan satu sama lain.
Versi lain yang tampaknya berfungsi adalah TimeZone independen, tetapi hanya berfungsi karena dua masalah membatalkan sendiri. Pertama, seperti yang dijelaskan di atas, timestamp without time zone AT TIME ZONE
menafsirkan ulang stempel waktu berada di zona waktu tersebut untuk konversi ke stempel waktu UTC; ini secara efektif mengurangi offset zona waktu.
Namun, untuk alasan yang tidak saya ketahui, PostgreSQL menggunakan stempel waktu dengan tanda kebalikan dari apa yang biasa saya lihat di sebagian besar tempat. Lihat dokumentasi:
Masalah lain yang perlu diingat adalah bahwa dalam nama zona waktu POSIX, offset positif digunakan untuk lokasi di sebelah barat Greenwich. Di tempat lain, PostgreSQL mengikuti konvensi ISO-8601 bahwa offset zona waktu positif berada di sebelah timur Greenwich.
Ini berarti bahwa EST5EDT
sama dengan +5
, bukan -5
. Itulah mengapa ini berhasil:karena Anda mengurangi offset tz bukan menambahkannya, tetapi Anda mengurangi offset yang dinegasikan!
Yang Anda perlukan untuk memperbaikinya adalah:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';