Postgres memiliki dua tipe data stempel waktu yang berbeda:
timestamp with time zone
, nama pendek:timestamptz
timestamp without time zone
, nama pendek:timestamp
timestamp
adalah pilihan ketik tanggal/waktu keluarga, secara harfiah. Ini memiliki typispreferred
atur di pg_type
, yang mungkin relevan:
- Membuat deret waktu antara dua tanggal di PostgreSQL
Penyimpanan internal dan zaman
Secara internal, stempel waktu menempati 8 byte penyimpanan di disk dan di 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 hitungan detik waktu UNIX yang umum digunakan 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:
* Timestamps, as well as the h/m/s fields of intervals, are stored as * int64 values with units of microseconds. (Once upon a time they were * double values with units of seconds.)
Dan:
/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */ #define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
Resolusi mikrodetik diterjemahkan ke maksimum 6 digit pecahan untuk detik.
timestamp
Untuk timestamp
tidak ada zona waktu yang diberikan secara eksplisit. Postgres mengabaikan pengubah zona waktu apa pun yang tidak sengaja ditambahkan ke input literal!
Tidak ada jam yang digeser untuk ditampilkan. Dengan semua yang terjadi di zona waktu yang sama, ini baik-baik saja. Untuk zona waktu yang berbeda artinya berubah, tetapi nilai dan tampilan tetap sama.
timestamp
Penanganan timestamptz
secara halus berbeda. Saya mengutip manualnya di sini:
Untuk
timestamp with time zone
, nilai yang disimpan secara internal selalu dalam UTC (Waktu Koordinat Universal ...)
Penekanan saya yang berani. Zona waktu itu sendiri tidak pernah disimpan . Ini adalah pengubah input yang digunakan untuk menghitung stempel waktu UTC yang sesuai, yang disimpan - atau dan dekorator keluaran yang digunakan untuk menghitung waktu lokal untuk tampilan - dengan offset zona waktu yang ditambahkan. Jika Anda tidak menambahkan offset untuk timestamptz
pada input, pengaturan zona waktu sesi saat ini diasumsikan. Semua perhitungan dilakukan dengan nilai stempel waktu UTC. Jika Anda (mungkin) harus berurusan dengan lebih dari satu zona waktu, gunakan timestamptz
. Dengan kata lain:Jika ada keraguan atau kesalahpahaman tentang zona waktu yang diasumsikan, gunakan timestamptz
. Berlaku di sebagian besar kasus penggunaan.
Klien seperti psql atau pgAdmin atau aplikasi apa pun yang berkomunikasi melalui libpq (seperti Ruby dengan permata pg) disajikan dengan stempel waktu plus offset untuk zona waktu saat ini atau sesuai dengan diminta zona waktu (lihat di bawah). Itu selalu titik waktu yang sama , hanya format tampilan yang bervariasi. Atau, seperti yang dikatakan manual:
Semua tanggal dan waktu yang sadar zona waktu disimpan secara internal di UTC. Mereka dikonversi ke waktu lokal di zona yang ditentukan oleh
TimeZone
parameter konfigurasi sebelum ditampilkan ke klien.
Contoh di psql:
db=# SELECT timestamptz '2012-03-05 20:00+03';
timestamptz
------------------------
2012-03-05 18:00:00+01
Apa yang terjadi di sini?
Saya memilih offset zona waktu arbitrer +3
untuk input literal. Untuk 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 waktu musim panas ("waktu musim panas", DST). Jadi 2012-03-05 18:00:00+01
karena DST hanya berlaku nanti.
Postgres segera melupakan input literal. Yang diingatnya hanyalah nilai untuk tipe data. Sama seperti dengan angka desimal. numeric '003.4'
atau numeric '+3.4'
- keduanya menghasilkan nilai internal yang sama persis.
AT TIME ZONE
Semua yang hilang sekarang, adalah alat untuk menafsirkan atau mewakili literal cap waktu menurut zona waktu tertentu. Di situlah AT TIME ZONE
membangun masuk Ada dua kasus penggunaan yang berbeda. timestamp
diubah menjadi timestamp
dan sebaliknya.
Untuk memasukkan timestamptz
UTC 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'
Itu benar, AT TIME ZONE 'UTC'
dua kali . Yang pertama menafsirkan timestamp
nilai sebagai (diberikan) stempel waktu UTC yang mengembalikan jenis timestamptz
. Yang kedua mengonversi timestamptz
ke timestamp
di zona waktu tertentu 'EST' - apa yang ditampilkan jam dinding di zona waktu EST pada titik waktu ini.
Contoh
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) identik baris dengan stempel waktu kolom yang memiliki stempel waktu UTC yang sama 2012-03-05 17:00:00
. Baris ke-9 kebetulan bekerja di zona waktu saya, tetapi merupakan jebakan jahat. Lihat di bawah.
Baris 6 - 8 dengan zona waktu nama dan zona waktu singkatan untuk waktu Hawaii tunduk pada DST (waktu musim panas) dan mungkin berbeda, meskipun tidak saat ini. Nama zona waktu seperti 'US/Hawaii'
mengetahui aturan DST dan semua riwayat berubah secara otomatis, sementara singkatan seperti HST
hanyalah kode bodoh untuk offset tetap. Anda mungkin perlu menambahkan singkatan yang berbeda untuk musim panas/waktu standar. nama menafsirkan dengan benar apa saja stempel waktu pada zona waktu tertentu. Sebuah singkatan murah, tetapi harus tepat untuk stempel waktu yang diberikan:
- Nama zona waktu dengan properti identik menghasilkan hasil yang berbeda saat diterapkan pada stempel waktu
Waktu Musim Panas bukanlah salah satu ide paling cemerlang yang pernah dibuat manusia.
Baris 9, ditandai sebagai kaki yang terisi bekerja untuk saya , tapi hanya kebetulan. Jika Anda secara eksplisit melemparkan literal ke timestamp [without time zone]
, offset zona waktu apa pun diabaikan! Hanya cap waktu kosong yang digunakan. Nilai tersebut kemudian secara otomatis dipaksa ke 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 berikan timestamptz
literal ke timestamp
atau Anda kehilangan offset zona waktu.
Pertanyaan Anda
Pengguna menyimpan waktu, katakanlah 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 kueri 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 menurut jam lokal:
SELECT * FROM tbl WHERE time_col > now()::time
Belum bosan dengan informasi latar belakang? Ada lebih banyak di manual.