Ö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:
- Bir kullanıcının saat diliminin adını kullanıcı tercihi olarak saklayın (ör .
America/Los_Angeles
Değil -0700
).
- 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
).
- Uygulamada, zamanı
UTC
bir TIMESTAMP WITH TIME ZONE
sütuna dönüştürün ve saklayın.
- İade saati, bir kullanıcının saat dilimine göre yerel olarak istekler (ör
UTC
.America/Los_Angeles
).
- Veritabanı var Set
timezone
iç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 ZONE
hafif 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. CHECK
Zaman 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 ZONE
hedefinizin referans çerçevesini (UTC
) .
Tamamlanmamış bir dönüştürürken TIMESTAMP WITHOUT TIME ZONE
bir 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
UTC
olduğu gibi kullanıntimezone
Rastgele programlama dili notu: Python'un datetime
veri 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 ZONE
ideal 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 ZONE
kaydetmiş 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-2008
Pasifik Standart Saati (+8000
) olacaktı, ancak 2005 yılı + saat dilimi veri tabanlarından 10-31-2008
itibaren 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 ZONE
ve TIME WITH TIME ZONE
tüm göreli veri tipleridir. Örneğin: '2011-05-28'::DATE
gece 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'::TIME
görelidir çünkü ne zaman dilimini ne de zamanın DATE
temsil ettiğini bilmiyorsunuz . Bununla bile '23:59:59-07'::TIME WITH TIME ZONE
, ne DATE
olacağını bilmiyorsun . Ve son olarak, DATE
bir saat dilimi ile aslında a DATE
değ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.