Bir PostgreSQL veritabanında hangi zaman damgası türünü seçmeliyim?


119

Çok zaman dilimli bir proje bağlamında Postgres veritabanımda zaman damgalarını depolamak için bir en iyi uygulama tanımlamak istiyorum.

yapabilirim

  1. TIMESTAMP WITHOUT TIME ZONEBu alan için ekleme zamanında hangi saat diliminin kullanıldığını seçin ve hatırlayın
  2. TIMESTAMP WITHOUT TIME ZONEekleme zamanında kullanılan saat diliminin adını içerecek başka bir alan seçin ve ekleyin
  3. TIMESTAMP WITH TIME ZONEzaman damgalarını buna göre seçin ve ekleyin

3. Seçeneği (saat dilimli zaman damgası) biraz tercih ediyorum, ancak konuyla ilgili bilgili bir fikir sahibi olmak istiyorum.

Yanıtlar:


142

Öncelikle, PostgreSQL'in zaman işleme ve aritmetiği harika ve genel durumda Seçenek 3 gayet iyi. Bununla birlikte, zaman ve saat dilimlerinin eksik bir görünümüdür ve tamamlanabilir:

  1. Bir kullanıcının saat diliminin adını kullanıcı tercihi olarak saklayın (ör . America/Los_AngelesDeğil -0700).
  2. Kullanıcı olaylarının / zaman verilerinin kendi referans çerçevelerine göre yerel olarak gönderilmesini sağlayın (büyük olasılıkla UTC'den bir sapma, örneğin -0700).
  3. Uygulamada, zamanı UTCbir TIMESTAMP WITH TIME ZONEsütuna dönüştürün ve saklayın.
  4. İade saati, bir kullanıcının saat dilimine göre yerel olarak istekler (ör UTC.America/Los_Angeles ).
  5. Veritabanı var Set timezoneiçin UTC.

Bu seçenek her zaman işe yaramaz çünkü bir kullanıcının saat dilimini ve dolayısıyla TIMESTAMP WITH TIME ZONEhafif uygulamalarda kullanmak için hedge tavsiyesini almak zor olabilir . Bununla birlikte, bu Seçenek 4'ün bazı arka plan yönlerini daha ayrıntılı olarak açıklamama izin verin.

Seçenek 3 gibi, bunun nedeni WITH TIME ZONE, bir şeyin meydana geldiği zamanın mutlak bir zaman anı olmasıdır. akrabaWITHOUT TIME ZONE verir saat dilimi . Asla, asla mutlak ve göreceli TIMESTAMP'leri karıştırmayın.

Programlı ve tutarlılık açısından, tüm hesaplamaların saat dilimi olarak UTC kullanılarak yapıldığından emin olun. Bu bir PostgreSQL gereksinimi değildir, ancak diğer programlama dilleri veya ortamları ile entegre olurken yardımcı olur. CHECKZaman damgası sütununa yazmanın bir saat dilimi ofsetine sahip olduğundan emin olmak için sütunda bir değerin ayarlanması 0, birkaç hata sınıfını önleyen bir savunma konumudur (örneğin, bir komut dosyası verileri bir dosyaya döker ve başka bir şey zaman verilerini bir sözcük sıralaması). Yine, PostgreSQL'in tarih hesaplamalarını doğru bir şekilde yapması veya zaman dilimleri arasında dönüştürme yapması için buna ihtiyacı yoktur (yani, PostgreSQL, herhangi iki rastgele zaman dilimi arasındaki zamanları dönüştürmede çok ustadır). Veritabanına giren verilerin sıfır ofset ile saklandığından emin olmak için:

CREATE TABLE my_tbl (
  my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR:  new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1

% 100 mükemmel değil, ancak verilerin zaten UTC'ye dönüştürüldüğünden emin olmak için yeterince güçlü bir ayak atışına karşı önlem sağlıyor. Bunun nasıl yapılacağına dair pek çok fikir var, ancak bu benim deneyimlerime göre pratikte en iyisi gibi görünüyor.

Veritabanı zaman dilimi işlemeye yönelik eleştiriler büyük ölçüde haklıdır (bunu büyük bir yetersizlikle ele alan birçok veritabanı vardır), ancak PostgreSQL'in zaman damgalarını ve saat dilimlerini işlemesi oldukça harikadır (burada ve orada birkaç "özelliğe" rağmen). Örneğin, böyle bir özellik:

-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 15:47:58.138995-07
(1 row)

test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:02.235541
(1 row)

AT TIME ZONE 'UTC'Saat dilimi bilgilerini çıkardığını ve TIMESTAMP WITHOUT TIME ZONEhedefinizin referans çerçevesini (UTC ) .

Tamamlanmamış bir dönüştürürken TIMESTAMP WITHOUT TIME ZONEbir etmek TIMESTAMP WITH TIME ZONE, eksik saat dilimi bağlantı devralınan:

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
        -7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
        -7
(1 row)

-- Now change to UTC    
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 22:48:40.540119+00
(1 row)

-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:49.444446
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
         0
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
         0
(1 row)

Alt çizgi:

  • bir kullanıcının saat dilimini bir adlandırılmış etiket (ör. America/Los_Angeles) olarak saklayın ve UTC'den uzaklığı (ör.-0700 )
  • Sıfır olmayan bir ofset depolamak için zorunlu bir neden olmadıkça her şey için UTC kullanın
  • sıfır olmayan tüm UTC zamanlarını bir girdi hatası olarak ele alın
  • göreli ve mutlak zaman damgalarını asla karıştırmayın ve eşleştirmeyin
  • mümkünse veritabanında UTColduğu gibi kullanıntimezone

Rastgele programlama dili notu: Python'un datetimeveri türü, mutlak ve göreli zamanlar arasındaki farkı korumada çok iyidir ( PyTZ gibi bir kitaplıkla tamamlayana kadar ilk başta sinir bozucu olsa da ).


DÜZENLE

Göreceli ile mutlak arasındaki farkı biraz daha açıklayayım.

Mutlak zaman, bir olayı kaydetmek için kullanılır. Örnekler: "Kullanıcı 123 oturum açtı" veya "mezuniyet törenleri 2011-05-28 14: 00 PST'de başlıyor." Yerel saat diliminizden bağımsız olarak, olayın meydana geldiği yere ışınlanabilirseniz, olaya tanık olabilirsiniz. Bir veritabanındaki çoğu zaman verisi mutlaktır (ve bu nedenle TIMESTAMP WITH TIME ZONEideal olarak +0 uzaklık ve belirli bir saat dilimini yöneten kuralları temsil eden metinsel bir etiket ile olmalıdır - bir fark değil).

Göreceli bir olay, bir şeyin zamanını henüz belirlenmemiş bir zaman dilimi perspektifinden kaydetmek veya programlamak olacaktır. Örnekler: "İşletmemizin kapıları sabah 8'de açılıyor ve akşam 21'de kapanıyor", "hadi her pazartesi sabah 7'de haftalık kahvaltı toplantısı için buluşalım" veya "her Cadılar Bayramı akşam 8'de." Genel olarak, olaylar için bir şablonda veya fabrikada göreceli zaman kullanılır ve hemen hemen her şey için mutlak zaman kullanılır. Göreli zamanların değerini göstermesi gereken, işaret etmeye değer nadir bir istisna vardır. Bir şeyin meydana gelebileceği mutlak zaman konusunda belirsizliğin olabileceği, gelecekte yeterince uzak olan gelecekteki olaylar için göreceli bir zaman damgası kullanın. İşte gerçek bir dünya örneği:

2004 yılı olduğunu ve 31 Ekim 2008'de ABD'nin Batı Kıyısı'nda (yani America/Los_Angeles/ PST8PDT) bir teslimat planlamanız gerektiğini varsayalım . Bunu kullanarak mutlak zamanı kullanarak ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONEkaydetmiş olsaydınız, teslimat saat 2'de ortaya çıkardı çünkü ABD Hükümeti, yaz saati uygulamasını düzenleyen kuralları değiştiren 2005 Enerji Politikası Yasasını kabul etti . Teslimatın planlandığı 2004 yılında, tarih 10-31-2008Pasifik Standart Saati (+8000 ) olacaktı, ancak 2005 yılı + saat dilimi veri tabanlarından 10-31-2008itibaren Pasifik Gün Işığından Yararlanma saati (+0700). Göreceli bir zaman damgasını saat dilimiyle birlikte saklamak, doğru bir teslimat programı ile sonuçlanırdı çünkü göreceli bir zaman damgası, Kongre'nin yanlış bilgilendirilmiş kurcalamalarına karşı bağışıktır. İşleri planlamak için göreceli ve mutlak zamanları kullanmak arasındaki kesinti belirsiz bir çizgi ise, benim temel kuralım, gelecekte 3-6 aydan daha ileri herhangi bir şey için zamanlama yapmanın göreceli zaman damgalarını kullanması gerektiğidir (planlanmış = mutlak ve planlanmış = göreceli ???).

Diğer / son göreli zaman türü INTERVAL. Örnek: "Oturum, kullanıcı oturum açtıktan 20 dakika sonra zaman aşımına uğrayacaktır". An INTERVAL, mutlak zaman damgaları ( TIMESTAMP WITH TIME ZONE) veya göreli zaman damgaları (TIMESTAMP WITHOUT TIME ZONE . "Bir kullanıcı oturumu başarılı bir oturum açtıktan 20 dakika sonra sona erer (login_utc + session_duration)" veya "sabah kahvaltısı toplantımız yalnızca 60 dakika sürebilir (tekrarlayan_başlangıç_saati + toplantı_uzunluğu)" demek de aynı derecede doğrudur.

Karışıklık Son bitleri: DATE, TIME, TIME WITHOUT TIME ZONEve TIME WITH TIME ZONEtüm göreli veri tipleridir. Örneğin: '2011-05-28'::DATEgece yarısını tanımlamak için kullanılabilecek saat dilimi bilginiz olmadığı için göreli bir tarihi temsil eder. Benzer şekilde, '23:23:59'::TIMEgörelidir çünkü ne zaman dilimini ne de zamanın DATEtemsil ettiğini bilmiyorsunuz . Bununla bile '23:59:59-07'::TIME WITH TIME ZONE, ne DATEolacağını bilmiyorsun . Ve son olarak, DATEbir saat dilimi ile aslında a DATEdeğil, a TIMESTAMP WITH TIME ZONE:

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 07:00:00
(1 row)

test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 00:00:00
(1 row)

Veritabanlarına tarih ve saat dilimlerini koymak iyi bir şeydir, ancak ince bir şekilde yanlış sonuçlar elde etmek kolaydır. Zaman bilgisini doğru ve tam olarak saklamak için minimum ek çaba gerekir, ancak bu her zaman fazladan çaba gerektiği anlamına gelmez.


2
Postgresql'e kullanıcının zaman damgasının doğru zaman dilimini doğru bir şekilde söylerseniz, postgresql arka planda işin zor kısmını halledecektir. Kendin dönüştürmek sadece belayı ödünç almaktır.
Seth Robertson

1
@Sean - kontrol kısıtlamanızla, bir zaman damgası olmadan nasıl bir zaman damgası eklersiniz set timezone to 'UTC'? Bunu bilmen tüm zaman dilimi farkında tarihleri UTC dahili olarak depolanır ?

2
Kontrolün amacı, verilerin UTC'den sıfır sapması ile saklandığından emin olmaktır. Bilginin sıralanması ve geri çağrılması ve zamanların sıfır olmayan uzaklıklarla karşılaştırılması hataya açıktır. Sıfır UTC farkını uygulayarak, tüm senaryolarda öngörülebilir şekilde davranan neredeyse sıfır riskli bir şekilde tek bir perspektiften verilerle tutarlı bir şekilde etkileşim kurabilirsiniz. Zaman damgalarının saat dilimlerinin metinsel temsillerini desteklemesi pratik olsaydı, konu hakkındaki düşüncelerim farklı olurdu. : ~]
Sean

6
@Sean: Ancak, Jack'in belirttiği gibi, saat dilimine duyarlı tüm zaman damgaları temelde UTC'de dahili olarak depolanır ve kullanıldıklarında yerel saat diliminize dönüştürülür; etkili bir şekilde, ayıklama (... dan saat dilimi) o zaman daima bağlantının yerel saat dilimi ne olursa olsun döndürür: zaman damgasının nasıl "depolandığıyla" hiçbir ilgisi yoktur. Başka bir deyişle, saat dilimi türünün bir parçası değildir ve depolanamaz: "saat dilimiyle", yalnızca verilerin diğer türlerle etkileşime girdiğinde nasıl dönüştürüleceğinin bir özelliğidir. Dolayısıyla veriler, metinsel veya başka türlü, saat dilimlerinin hiçbir temsiline sahip değildir.
Jay Freeman -saurik-

@ JayFreeman-saurik-: Kesinlikle haklısınız. '' CHECK () '' olası tehlikeli koda karşı koruma sağlamak için bir anti-ayak vuruşu önlemi olarak var. Verilerin yazılırken UTC olduğundan emin olmak, kodun üzerinde düşünüldüğüne veya yürütme ortamının doğru kurulduğuna dair mütevazı bir garanti sağlar.
Sean

59

Sean'ın cevabı aşırı derecede karmaşık ve yanıltıcı.

Gerçek şu ki, hem "SAAT DİLİMİ İLE" hem de "ZAMAN DİLİMİ OLMADAN" değeri, unix benzeri mutlak UTC zaman damgası olarak depolar. Aradaki fark, zaman damgasının nasıl görüntülendiğidir. "İLE saat diliminde" olduğunda görüntülenen değer, kullanıcının bölgesine çevrilen UTC saklanan değerdir. "Saat dilimi OLMADAN" olduğunda, UTC saklanan değer, kullanıcı hangi bölgeyi ayarlamış olursa olsun aynı saat yüzünü gösterecek şekilde bükülür ".

"OLMADAN saat diliminin" kullanılabildiği tek durum, gerçek bölgeden bağımsız olarak bir saat yüzü değerinin uygulanabilir olmasıdır. Örneğin, bir zaman damgası, oylama kabinlerinin ne zaman kapanacağını gösterdiğinde (yani, bir kişinin saat dilimine bakılmaksızın 20: 00'da kapanırlar).

3. seçeneği kullanın. Bunu yapmamak için çok özel bir neden olmadıkça her zaman "İLE saat dilimi" seçeneğini kullanın.


10
Önde gelen bir Postgres uzmanı olan David E. Wheeler, değerlendirmenizin " Her Zaman ZAMAN DİLİMİNDE TIMESTAMP İLE KULLANIN" başlıklı gönderisine göre hemfikirdir .
Basil Bourque

2
Ya tarayıcınızın UTC zaman damgasını yerel saat dilimine dönüştürmesini isterseniz? Dolayısıyla, db dönüşümü asla yapmayacak ve yalnızca UTC'yi içerecektir. "Saat dilimi OLMADAN" kabul edilebilir mi?
dman

5

Postgres sizin için zaman dilimine göre zaman damgalarını yeniden hesaplayan tüm işi yapabildiğinden, diğer ikisiyle bunu kendiniz yapmanız gerekeceğinden, tercihim 3. seçeneğe doğru. Zaman damgasını bir saat dilimiyle depolamanın fazladan depolama ek yükü, milyonlarca kayıttan bahsetmiyorsanız gerçekten ihmal edilebilir, bu durumda muhtemelen zaten oldukça etli depolama gereksinimleriniz vardır.


19
Yanlış. Ek yük yoktur… Postgres saat dilimini saklamaz (bu arada 'uzaklık' doğru terimdir, saat dilimi değil). TIMESTAMP WITH TIME ZONEAdı yanıltıcı. Gerçekten, "eklerken / güncellerken belirtilen herhangi bir ofsete dikkat edin ve tarih-saati UTC'ye ayarlamak için bu ofseti kullanın" anlamına gelir. TIMESTAMP WITHOUT TIME ZONEAdı vasıtaları "Herhangi o ekleme / güncelleme sırasında mevcut olabilir ofset ayarlama gerektirmeden UTC olarak tarih ve saat bölümlerini dikkate görmezden". Belgeyi dikkatlice okuyun .
Basil Bourque

1
@BasilBourque bu bilgi için teşekkür ederim. İnanılmaz derecede kullanışlı. Bunu okuyan diğerleri için belgedeki satırı şöyle der: "Saat dilimi olmadan zaman damgası olduğu belirlenen bir değişmez değerde, PostgreSQL herhangi bir saat dilimi gösterimini sessizce yok sayar. Yani, sonuç olarak elde edilen değer, içindeki tarih / saat alanlarından türetilir. giriş değeri ve saat dilimine göre ayarlanmadı. "
Aidan Rosswood
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.