Tipe data timestamp
adalah nama pendek untuk timestamp without time zone
.
Opsi lainnya timestamptz
adalah kependekan dari timestamp with time zone
.
timestamptz
adalah tipe yang disukai dalam keluarga tanggal / waktu, secara harfiah. Ini telah typispreferred
diatur pg_type
, yang mungkin relevan:
Penyimpanan internal dan zaman
Secara internal, cap waktu menempati 8 byte penyimpanan pada disk dan dalam RAM. Ini adalah nilai integer yang mewakili hitungan mikrodetik dari zaman Postgres, 2000-01-01 00:00:00 UTC.
Postgres juga memiliki pengetahuan bawaan tentang UNIX penghitungan waktu yang biasa digunakan detik dari zaman UNIX, 1970-01-01 00:00:00 UTC, dan menggunakannya dalam fungsi to_timestamp(double precision)
atau EXTRACT(EPOCH FROM timestamptz)
.
Kode sumber:
* Stempel waktu, serta bidang interval h / m / s, disimpan sebagai
* Nilai int64 dengan satuan mikrodetik. (Dahulu kala mereka
* nilai ganda dengan satuan detik.)
Dan:
/ * Setara dengan Julian-date pada Hari 0 di perhitungan Unix dan Postgres * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
Resolusi mikrodetik diterjemahkan hingga maksimum 6 digit fraksional selama detik.
timestamp
Nilai yang diketikkan memberitahu Postgres bahwa tidak ada zona waktu yang disediakan secara eksplisit. Zona waktu saat ini diasumsikan. Postgres mengabaikan pengubah zona waktu yang ditambahkan karena kesalahan!timestamp
[without time zone]
Tidak ada jam yang digeser untuk tampilan. Dengan pengaturan zona waktu yang sama semuanya baik-baik saja. Untuk zona waktu yang berbeda, pengaturan artinya berubah, tetapi nilai dan tampilan tetap sama.
timestamptz
Penanganannya timestamp with time zone
agak berbeda. Saya mengutip manualnya di sini :
Sebab timestamp with time zone
, nilai yang disimpan secara internal selalu dalam UTC (Waktu Koordinasi Universal ...)
Penekanan berani saya. The zona waktu itu sendiri tidak pernah disimpan . Ini adalah pengubah input yang digunakan untuk menghitung stempel waktu UTC yang sesuai, yang disimpan - atau dan pengubah output yang digunakan untuk menghitung waktu lokal untuk ditampilkan - dengan offset zona waktu yang ditambahkan. Jika Anda tidak menambahkan offset untuk timestamptz
input, pengaturan zona waktu sesi saat ini diasumsikan. Semua perhitungan dilakukan dengan nilai timestamp UTC. Jika Anda harus (atau mungkin harus) berurusan dengan lebih dari satu zona waktu, gunakan timestamptz
.
Klien seperti psql atau pgAdmin atau aplikasi apa pun yang berkomunikasi melalui libpq (seperti Ruby dengan permata pg) disajikan dengan cap waktu plus offset untuk zona waktu saat ini atau sesuai dengan zona waktu yang diminta (lihat di bawah). Itu selalu merupakan titik waktu yang sama , hanya format tampilan yang bervariasi. Atau, seperti yang dikatakan manual :
Semua tanggal dan waktu sadar zona waktu disimpan secara internal di UTC. Mereka dikonversi ke waktu lokal di zona yang ditentukan oleh
parameter konfigurasi TimeZone sebelum ditampilkan ke klien.
Pertimbangkan contoh sederhana ini (dalam psql):
db = # SELECT timestamptz '2012-03-05 20:00 +03 ';
timestamptz
------------------------
2012-03-05 18:00:00 +01
Penekanan berani saya. Apa yang terjadi disini?
Saya memilih offset zona waktu sembarang +3
untuk input literal. Bagi Postgres, ini hanyalah salah satu dari banyak cara untuk memasukkan stempel waktu UTC 2012-03-05 17:00:00
. Hasil kueri ditampilkan untuk pengaturan zona waktu saat ini Wina / Austria dalam pengujian saya, yang memiliki offset +1
selama musim dingin dan +2
selama musim panas:, 2012-03-05 18:00:00+01
karena jatuh ke waktu musim dingin.
Postgres sudah lupa bagaimana nilai ini dimasukkan. Yang diingat hanyalah nilai dan tipe data. Sama seperti dengan angka desimal. numeric '003.4'
, numeric '3.40'
atau numeric '+3.4'
- semua menghasilkan nilai internal yang sama persis.
AT TIME ZONE
Segera setelah Anda memahami logika ini, Anda dapat melakukan apa pun yang Anda inginkan. Semua yang hilang sekarang, adalah alat untuk menafsirkan atau mewakili timestamp literal sesuai dengan zona waktu tertentu. Di situlah AT TIME ZONE
konstruk masuk. Ada dua kasus penggunaan yang berbeda. timestamptz
dikonversi ke timestamp
dan sebaliknya.
Untuk memasuki UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... yang setara dengan:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Untuk menampilkan titik waktu yang sama dengan EST timestamp
(Waktu Standar Timur):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Benar, AT TIME ZONE 'UTC'
dua kali . Yang pertama menginterpretasikan timestamp
nilai sebagai (diberikan) cap waktu UTC mengembalikan tipe timestamptz
. Yang kedua mengkonversi timestamptz
ke timestamp
dalam zona waktu yang diberikan 'EST' - apa yang ditampilkan jam di zona waktu EST pada titik waktu unik ini.
Contohnya
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Mengembalikan 8 (atau 9) baris identik dengan kolom timestamptz yang memegang stempel waktu UTC yang sama 2012-03-05 17:00:00
. Jenis baris ke-9 kebetulan bekerja di zona waktu saya, tetapi merupakan perangkap jahat. Lihat di bawah.
① Baris 6 - 8 dengan nama zona waktu dan singkatan zona waktu untuk waktu Hawaii tunduk pada DST (waktu musim panas) dan mungkin berbeda, meskipun saat ini tidak. Nama zona waktu seperti 'US/Hawaii'
menyadari aturan DST dan semua pergeseran historis secara otomatis, sedangkan singkatan seperti HST
hanyalah kode bodoh untuk offset tetap. Anda mungkin perlu menambahkan singkatan yang berbeda untuk musim panas / waktu standar. The nama dengan benar menafsirkan setiap timestamp pada zona waktu yang diberikan. Sebuah singkatan murah, tapi perlu menjadi orang yang tepat untuk cap waktu yang diberikan:
Daylight Saving Time bukan salah satu ide paling cerdas yang pernah muncul.
② Baris 9, ditandai sebagai footgun dimuat bekerja untuk saya , tetapi hanya karena kebetulan. Jika Anda secara eksplisit memasukkan literal ke timestamp [without time zone]
, offset zona waktu apa pun diabaikan ! Hanya cap waktu kosong yang digunakan. Nilai tersebut kemudian secara otomatis dipaksa timestamptz
dalam contoh untuk mencocokkan jenis kolom. Untuk langkah ini, timezone
pengaturan sesi saat ini diasumsikan, yang kebetulan merupakan zona waktu yang sama +1
dalam kasus saya (Eropa / Wina). Tetapi mungkin tidak dalam kasus Anda - yang akan menghasilkan nilai yang berbeda. Singkatnya: Jangan melemparkan timestamptz
literal ke timestamp
atau Anda kehilangan offset zona waktu.
Pertanyaan Anda
Pengguna menyimpan waktu, katakan 17 Maret 2012, 19:00. Saya tidak ingin konversi zona waktu atau zona waktu disimpan.
Zona waktu itu sendiri tidak pernah disimpan. Gunakan salah satu metode di atas untuk memasukkan stempel waktu UTC.
Saya hanya menggunakan zona waktu yang ditentukan pengguna untuk mendapatkan catatan 'sebelum' atau 'setelah' waktu saat ini di zona waktu lokal pengguna.
Anda dapat menggunakan satu permintaan untuk semua klien di zona waktu yang berbeda.
Untuk waktu global absolut:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Untuk waktu sesuai dengan jam lokal:
SELECT * FROM tbl WHERE time_col > now()::time
Belum bosan dengan informasi latar belakang, belum? Ada lebih banyak di manual.