PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Mengabaikan zona waktu sama sekali di Rails dan PostgreSQL

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tabel dengan koordinat kisi heksagonal yang menutupi dunia

  2. Instal Postgres.app di Mac

  3. Menyebarkan Django + Python 3 + PostgreSQL ke AWS Elastic Beanstalk

  4. Mengubah tipe kolom menjadi string yang lebih panjang di Rails

  5. 9.6 Turnamen Patch Paling Menakutkan