Perbedaan antara cap waktu dengan / tanpa zona waktu di PostgreSQL

186

Apakah nilai stempel waktu disimpan secara berbeda di PostgreSQL saat tipe data WITH TIME ZONEdibandingkan WITHOUT TIME ZONE? Bisakah perbedaan diilustrasikan dengan kasus uji sederhana?

Larsenal
sumber
3
Jawaban terkait ini mungkin bisa membantu.
Erwin Brandstetter

Jawaban:

157

Perbedaannya tercakup pada dokumentasi PostgreSQL untuk tipe tanggal / waktu . Ya, perawatan TIMEatau TIMESTAMPberbeda antara satu WITH TIME ZONEatau WITHOUT TIME ZONE. Itu tidak mempengaruhi bagaimana nilai-nilai disimpan; itu mempengaruhi bagaimana mereka ditafsirkan.

Efek zona waktu pada tipe data ini dicakup secara khusus dalam dokumen. Perbedaan muncul dari apa yang dapat diketahui oleh sistem tentang nilai:

  • Dengan zona waktu sebagai bagian dari nilai, nilai dapat diberikan sebagai waktu lokal di klien.

  • Tanpa zona waktu sebagai bagian dari nilai, zona waktu default yang jelas adalah UTC, sehingga diberikan untuk zona waktu itu.

Perilaku berbeda tergantung pada setidaknya tiga faktor:

  • Pengaturan zona waktu di klien.
  • Jenis data (yaitu WITH TIME ZONEatau WITHOUT TIME ZONE) dari nilai.
  • Apakah nilainya ditentukan dengan zona waktu tertentu.

Berikut adalah contoh yang mencakup kombinasi faktor-faktor tersebut:

foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 00:00:00+09
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 06:00:00+09
(1 row)

foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 00:00:00+11
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 08:00:00+11
(1 row)
hidung besar
sumber
88
Hanya benar jika merujuk pada proses memasukkan / mengambil nilai. Tetapi pembaca harus memahami bahwa kedua tipe data, timestamp with time zonedan timestamp without time zone, di Postgres * tidak benar - benar menyimpan informasi zona waktu. Anda dapat mengkonfirmasi ini dengan melihat sekilas pada halaman dokumen tipe data: Kedua jenis ini menggunakan jumlah oktet yang sama dan memiliki rentang nilai penyimpanan, sehingga tidak ada ruang untuk menyimpan info zona waktu. Teks halaman mengkonfirmasi ini. Sesuatu yang keliru: "tanpa tz" berarti "abaikan offset saat memasukkan data" dan "dengan tz" berarti "gunakan offset untuk menyesuaikan ke UTC".
Basil Bourque
42
Tipe data keliru dalam cara kedua: Mereka mengatakan "zona waktu" tetapi sebenarnya kita berbicara tentang offset dari UTC / GMT. Zona waktu sebenarnya merupakan offset plus aturan / sejarah tentang Daylight Saving Time (DST) dan anomali lainnya.
Basil Bourque
4
Saya lebih suka mengatakan offset adalah zona waktu plus aturan untuk DST. Anda tidak dapat menemukan zona waktu yang diberikan offset, tetapi Anda dapat menemukan offset yang diberikan zona waktu dan aturan DST.
igorsantos07
3
Mengutip dokumen resmi : 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.
Guillaume Husta
2
@ igorsantos07 Zona waktu adalah seperangkat aturan / sejarah tentang perubahan DST dan perubahan lainnya. Kata-kata Anda sepertinya berlebihan. Dan pernyataan Anda bahwa "offset adalah zona waktu plus aturan untuk DST" sama sekali salah: offset hanya beberapa jam, menit, dan detik - tidak lebih, tidak kurang.
Basil Bourque
34

Saya mencoba menjelaskannya lebih dimengerti daripada dokumentasi PostgreSQL yang dimaksud.

Baik TIMESTAMPvarian menyimpan zona waktu (atau offset), meskipun apa nama menyarankan. Perbedaannya terletak pada interpretasi data yang disimpan (dan dalam aplikasi yang dimaksud), bukan dalam format penyimpanan itu sendiri:

  • TIMESTAMP WITHOUT TIME ZONEmenyimpan waktu tanggal lokal (alias. tanggal kalender dinding dan waktu jam dinding). Zona waktunya tidak ditentukan sejauh yang PostgreSQL tahu (meskipun aplikasi Anda mungkin tahu apa itu). Karenanya, PostgreSQL tidak melakukan konversi terkait zona waktu pada input atau output. Jika nilai dimasukkan ke dalam basis data sebagai '2011-07-01 06:30:30', maka tidak ada mater di zona waktu yang Anda tampilkan nanti, itu masih akan mengatakan tahun 2011, bulan 07, hari 01, 06 jam, 30 menit, dan 30 detik (dalam beberapa format). Juga, setiap offset atau zona waktu yang Anda tentukan dalam input diabaikan oleh PostgreSQL, jadi '2011-07-01 06:30:30+00'dan '2011-07-01 06:30:30+05'sama dengan adil '2011-07-01 06:30:30'. Untuk pengembang Java: analog dengan java.time.LocalDateTime.

  • TIMESTAMP WITH TIME ZONEmenyimpan titik pada garis waktu UTC. Kelihatannya (berapa jam, menit, dll.) Tergantung pada zona waktu Anda, tetapi selalu mengacu pada instan "fisik" yang sama (seperti saat peristiwa fisik aktual). Input secara internal dikonversi ke UTC, dan begitulah disimpan. Untuk itu, offset input harus diketahui, jadi ketika input tersebut tidak mengandung offset atau zona waktu eksplisit (seperti '2011-07-01 06:30:30') itu dianggap berada di zona waktu saat ini dari sesi PostgreSQL, jika tidak, zona offset atau zona waktu yang ditentukan secara eksplisit digunakan (seperti dalam '2011-07-01 06:30:30+05'). Outputnya ditampilkan dikonversi ke zona waktu saat ini dari sesi PostgreSQL. Untuk pengembang Java: Ini analog dengan java.time.Instant(dengan resolusi lebih rendah), tetapi dengan JDBC dan JPA 2.2 Anda seharusnya memetakannya ke java.time.OffsetDateTime(atau ke java.util.Dateataujava.sql.Timestamp tentu saja).

Ada yang mengatakan bahwa kedua TIMESTAMPvariasi menyimpan waktu tanggal UTC. Agak, tapi itu membingungkan menurut saya. TIMESTAMP WITHOUT TIME ZONEdisimpan seperti TIMESTAMP WITH TIME ZONE, yang diberikan dengan zona waktu UTC terjadi untuk memberikan tahun yang sama, bulan, hari, jam, menit, detik, dan mikrodetik seperti yang ada di waktu-tanggal lokal. Tapi itu tidak dimaksudkan untuk mewakili titik pada garis waktu yang menurut interpretasi UTC, itu hanya cara bidang tanggal waktu setempat dikodekan. (Ini adalah beberapa titik pada garis waktu, karena zona waktu sebenarnya bukan UTC; kami tidak tahu apa itu.)

ddekany
sumber
Tidak ada yang salah dengan mengambil TIMESTAMP WITH TIME ZONEa Instant. Keduanya mewakili titik pada timeline di UTC. Instantlebih disukai, menurut saya, lebih OffsetDateTimekarena lebih mendokumentasikan diri sendiri: A TIMESTAMP WITH TIME ZONEselalu diambil dari database sebagai UTC, dan Instantselalu dalam UTC sehingga cocok secara alami, sementara yang OffsetDateTimedapat membawa offset lainnya.
Basil Bourque
@BasilBourque Sayangnya, spesifikasi JDBC saat ini, spesifikasi JPA 2.2, dan juga dokumentasi JDBC PostgreSQL hanya menyebutkan OffsetDateTimesebagai tipe Java yang dipetakan. Saya tidak yakin apakah Instancemasih didukung di suatu tempat secara tidak resmi.
ddekany
pertanyaan, Anda mengatakan offset apa pun yang saya tentukan dalam input seperti '2011-07-01 06:30:30+00'dan '2011-07-01 06:30:30+05' diabaikan tapi saya bisa lakukan insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);dan itu akan mengubahnya menjadi utc dengan benar. di mana tanggal adalah timestamp tanpa zona waktu. Saya mencoba memahami apa nilai utama cap waktu dengan zona waktu dan mengalami masalah.
pk1m
@ pk1m Anda memperumit masalah dengan ::timestamptz. Dengan itu Anda mengonversi string menjadi TIMESTAMP WITH TIME ZONE, dan saat itu akan dikonversi lebih jauh WITHOUT TIME ZONE, yang akan menyimpan hari "kalender dinding" dan waktu jam dinding instan itu seperti yang terlihat dari zona waktu sesi Anda (yang mungkin UTC). Masih hanya akan menjadi timestamp lokal dengan offset yang tidak ditentukan (tanpa zona).
ddekany
Saya bekerja dengan python, dan itulah yang akan disisipkan ketika memasukkan objek data waktu timestamp sadar. Menurut saya ada nilai dalam menggunakan timestamp dengan zona waktu, tetapi tidak perlu untuk menangani zona waktu.
pk1m
12

Berikut adalah contoh yang dapat membantu. Jika Anda memiliki stempel waktu dengan zona waktu, Anda dapat mengubah stempel waktu itu menjadi zona waktu lainnya. Jika Anda belum memiliki zona waktu basis, zona waktu itu tidak akan dikonversi dengan benar.

SELECT now(),
   now()::timestamp,
   now() AT TIME ZONE 'CST',
   now()::timestamp AT TIME ZONE 'CST'

Keluaran:

-[ RECORD 1 ]---------------------------
now      | 2018-09-15 17:01:36.399357+03
now      | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
serby
sumber
5
Pernyataan "tidak akan dikonversi dengan benar" sama sekali tidak benar. Anda harus mengerti apa timestampdan timestamptzartinya. timestamptzberarti titik absolut dalam waktu (UTC) sedangkan timestampmenunjukkan apa yang ditunjukkan jam di zona waktu tertentu. Jadi, ketika mengkonversi timestamptzke zona waktu Anda bertanya apa yang ditunjukkan jam di New York pada titik waktu absolut ini? sedangkan ketika "mengubah" a timestamp, Anda bertanya apa titik absolut waktu ketika jam di New York menunjukkan x?
fphilipe
The AT TIME ZONEmembangun adalah otak teaser sendiri, bahkan jika Anda sudah memahami WITHvs WITHOUT TIME ZONEjenis. Jadi itu pilihan yang aneh untuk menjelaskannya. (: ( AT TIME ZONEmengubah WITH TIME ZONEcap waktu menjadi WITHOUT TIME ZONEcap waktu, dan sebaliknya ... tidak terlalu jelas.)
ddekany
now()::timestamp AT TIME ZONE 'CST'tidak masuk akal, kecuali Anda apa pada jam berapa CST 'zona waktu akan menunjukkan waktu bahwa jam lokal Anda saat ini menunjukkan
Jasen