Bagaimana cara mendapatkan cap waktu unix saat ini dari PostgreSQL?

91

Stempel waktu Unix adalah jumlah detik sejak tengah malam UTC 1 Januari 1970.

Bagaimana cara mendapatkan cap waktu unix yang benar dari PostgreSQL?

Ketika membandingkan dengan currenttimestamp.com dan timestamp.1e5b.de saya tidak mendapatkan waktu yang diharapkan dari PostgreSQL:

Ini mengembalikan stempel waktu yang benar:

SELECT extract(epoch from now());

Meskipun ini tidak:

SELECT extract(epoch from now() at time zone 'utc');

Saya tinggal di zona waktu UTC +02. Apa cara yang benar untuk mendapatkan cap waktu unix saat ini dari PostgreSQL?

Ini mengembalikan waktu dan zona waktu yang benar:

SELECT now();
              now
-------------------------------
 2011-05-18 10:34:10.820464+02

Perbandingan lain:

select now(), 
extract(epoch from now()), 
extract(epoch from now() at time zone 'utc');
              now              |    date_part     |    date_part
-------------------------------+------------------+------------------
 2011-05-18 10:38:16.439332+02 | 1305707896.43933 | 1305700696.43933
(1 row)

Unix timestamp from the web sites:
1305707967
Jonas
sumber

Jawaban:

82

Dalam postgres, timestamp with time zonedapat disingkat sebagai timestamptz, dan timestamp without time zonesebagai timestamp. Saya akan menggunakan nama jenis yang lebih pendek untuk kesederhanaan.

Mendapatkan cap waktu Unix dari postgres timestamptzlike now()itu sederhana, seperti yang Anda katakan, cukup:

select extract(epoch from now());

Itu benar-benar semua yang perlu Anda ketahui tentang mendapatkan waktu absolut dari jenis apa pun timestamptz, termasuk now().

Hal-hal hanya menjadi rumit ketika Anda memiliki timestampbidang.

Ketika Anda memasukkan timestamptzdata seperti itu now()ke bidang itu, pertama-tama data itu akan dikonversi ke zona waktu tertentu (baik secara eksplisit dengan at time zoneatau dengan mengonversi ke zona waktu sesi) dan informasi zona waktu dibuang . Itu tidak lagi mengacu pada waktu absolut. Inilah sebabnya mengapa Anda biasanya tidak ingin menyimpan cap waktu seperti timestampdan biasanya menggunakan timestamptz- mungkin sebuah film dirilis pada pukul 6 sore pada tanggal tertentu di setiap zona waktu , itulah jenis kasus penggunaan.

Jika Anda hanya pernah bekerja di zona waktu tunggal, Anda mungkin lolos dengan (mis) menggunakan timestamp. Konversi kembali ke timestamptzcukup pintar untuk mengatasi DST, dan cap waktu dianggap, untuk tujuan konversi, berada di zona waktu saat ini. Berikut ini contoh untuk GMT / BST:

select '2011-03-27 00:59:00.0+00'::timestamptz::timestamp::timestamptz
     , '2011-03-27 01:00:00.0+00'::timestamptz::timestamp::timestamptz;

/*
|timestamptz           |timestamptz           |
|:---------------------|:---------------------|
|2011-03-27 00:59:00+00|2011-03-27 02:00:00+01|
*/

DBFiddle

Tapi, perhatikan perilaku membingungkan berikut:

set timezone to 0;

values(1, '1970-01-01 00:00:00+00'::timestamp::timestamptz)
    , (2, '1970-01-01 00:00:00+02'::timestamp::timestamptz);

/*
|column1|column2               |
|------:|:---------------------|
|      1|1970-01-01 00:00:00+00|
|      2|1970-01-01 00:00:00+00|
*/

DBFiddle

Ini karena :

PostgreSQL tidak pernah memeriksa konten string literal sebelum menentukan jenisnya, dan karenanya akan memperlakukan keduanya [...] sebagai timestamp tanpa zona waktu. Untuk memastikan literal diperlakukan sebagai timestamp dengan zona waktu, berikan tipe eksplisit yang benar ... Dalam literal yang telah ditentukan sebagai timestamp tanpa zona waktu, PostgreSQL diam-diam akan mengabaikan indikasi zona waktu apa pun

Jack Douglas
sumber
Tahu bagaimana mengkonversi desimal yang dihasilkan menjadi bilangan bulat tanpa titik desimal (maksud saya menggabungkan angka dan desimal sebagai satu bilangan bulat besar). Terima kasih.
WM
Suka ini tapi saya yakin Anda tidak benar-benar ingin melakukan itu. Mungkin Anda ingin mengalikannya dengan kekuatan sepuluh dan menghapus desimal yang tersisa?
Jack Douglas
2
@ WM Mungkin seperti ini? SELECT FLOOR(EXTRACT(epoch FROM NOW())*1000);
Joe23
21
SELECT extract(epoch from now() at time zone 'utc');

tidak mengembalikan stempel waktu yang benar karena konversi zona waktu postgres membuang informasi zona waktu dari hasilnya:

9.9.3. PADA SAAT ZONA

Sintaks: timestamp tanpa zona waktu AT ZONA WAKTU Zona
Pengembalian: timestamp dengan zona waktu
Perlakukan stempel waktu yang diberikan tanpa zona waktu yang terletak di zona waktu yang ditentukan

Sintaks: timestamp dengan zona waktu AT ZONA WAKTU Zona
Pengembalian: timestamp tanpa zona waktu
Konversi cap waktu yang diberikan dengan zona waktu ke zona waktu baru, tanpa penunjukan zona waktu

setelah itu, ekstrak melihat stempel waktu tanpa zona waktu dan menganggapnya sebagai waktu lokal (meskipun sebenarnya sudah utc).

Cara yang benar adalah:

select now(),
       extract(epoch from now()),                                          -- correct
       extract(epoch from now() at time zone 'utc'),                       -- incorrect
       extract(epoch from now() at time zone 'utc' at time zone 'utc');    -- correct

          now                  |    date_part     |    date_part     |    date_part
-------------------------------+------------------+------------------+------------------
 2014-10-14 10:19:23.726908+02 | 1413274763.72691 | 1413267563.72691 | 1413274763.72691
(1 row)

Di baris terakhir yang pertama at time zonemelakukan konversi, yang kedua memberikan zona waktu baru ke hasilnya.

axil
sumber