Jawaban untuk timestamp
Anda perlu memahami sifat tipe data timestamp
(timestamp without time zone
) dan timestamptz
(timestamp with time zone
). Jika tidak, baca ini dulu:
- Mengabaikan zona waktu sama sekali di Rails dan PostgreSQL
AT TIME ZONE
konstruksi mengubah timestamp
ke timestamptz
, yang hampir pasti merupakan langkah yang salah untuk kasus Anda:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Pertama , itu membunuh kinerja. Menerapkan AT TIME ZONE
ke kolom eventtime
membuat ekspresi tidak sargable . Postgres tidak dapat menggunakan indeks biasa pada eventtime
. Tetapi bahkan tanpa indeks, ekspresi sargable lebih murah. Sesuaikan nilai filter daripada memanipulasi setiap nilai baris.
Anda bisa kompensasi dengan indeks ekspresi yang cocok, tapi itu mungkin hanya kesalahpahaman dan salah.
Apa yang terjadi dalam ekspresi itu?
-
AT TIME ZONE 'CET'
mengubahtimestamp
nilaieventtime
ketimestamptz
dengan menambahkan offset waktu dari zona waktu Anda saat ini. Saat menggunakan nama zona waktu (bukan offset numerik atau singkatan), ini juga memperhitungkan aturan DST (waktu musim panas), jadi Anda mendapatkan offset berbeda untuk cap waktu "musim dingin". Pada dasarnya Anda mendapatkan jawaban atas pertanyaan:Apa yang dimaksud dengan stempel waktu UTC yang sesuai untuk stempel waktu tertentu di zona waktu tertentu?
Saat menampilkan hasilnya kepada pengguna diformat sebagai stempel waktu lokal dengan offset waktu yang sesuai untuk zona waktu sesi saat ini. (Mungkin atau mungkin tidak sama dengan yang digunakan dalam ekspresi).
-
Literal string di sisi kanan tidak memiliki tipe data, jadi tipenya diturunkan dari penugasan dalam ekspresi. Karena itu
timestamptz
sekarang, keduanya dilemparkan ketimestamptz
, dengan asumsi zona waktu sesi saat ini.Berapa stempel waktu UTC yang sesuai untuk stempel waktu yang diberikan untuk setelan zona waktu sesi saat ini.
Offset dapat bervariasi dengan aturan DST.
Singkat cerita , jika Anda selalu beroperasi dengan zona waktu yang sama:CET
atau 'Europe/Berlin'
- hal yang sama untuk stempel waktu saat ini, tetapi tidak untuk stempel waktu bersejarah atau (mungkin) di masa mendatang, Anda bisa memotongnya.
Masalah kedua dengan ekspresi: hampir selalu salah dengan BETWEEN
timestamp
nilai-nilai. Lihat:
- Optimalkan pernyataan ANTARA tanggal
- Temukan rentang tanggal yang tumpang tindih di PostgreSQL
SELECT date_trunc('hour', eventtime) AS hour
, count(DISTINCT serialnumber) AS ct -- sure you need distinct?
FROM t_el_eventlog
WHERE eventtime >= now()::date - interval '18 hours'
AND eventtime < now()::date + interval '6 hours'
AND sourceid = 44 -- don't quote the numeric literal
GROUP BY 1
ORDER BY 1;
now()
adalah implementasi Postgres dari standar SQL CURRENT_TIMESTAMP
. Keduanya mengembalikan timestamptz
(bukan timestamp
!). Anda dapat menggunakan keduanya.now()::date
setara dengan CURRENT_DATE
. Keduanya bergantung pada pengaturan zona waktu saat ini.
Anda harus memiliki indeks dalam bentuk:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Atau, untuk mengizinkan pemindaian hanya indeks:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Jika Anda beroperasi di zona waktu yang berbeda, segalanya menjadi lebih rumit dan Anda harus menggunakan timestamptz
untuk semuanya.
Alternatif untuk timestamptz
Sebelum pembaruan pertanyaan, sepertinya zona waktu penting. Saat berhadapan dengan zona waktu yang berbeda, "hari ini" adalah ketergantungan fungsional dari zona waktu saat ini. Orang cenderung melupakan itu.
Untuk hanya bekerja dengan pengaturan zona waktu sesi saat ini, gunakan kueri yang sama seperti di atas. Jika dieksekusi di zona waktu yang berbeda, hasilnya sebenarnya salah. (Berlaku untuk hal di atas juga.)
Untuk menjamin hasil yang benar untuk zona waktu tertentu ('Eropa/Berlin' dalam kasus Anda) terlepas dari pengaturan zona waktu sesi saat ini, gunakan ekspresi ini sebagai gantinya:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Ketahuilah bahwa AT TIME ZONE
konstruksi mengembalikan timestamp
untuk timestamptz
masukan dan sebaliknya.
Seperti yang disebutkan di awal, semua detail mengerikan di sini:
- Mengabaikan zona waktu sama sekali di Rails dan PostgreSQL