PostgreSQL'de zaman dilimi olan / olmayan zaman damgaları arasındaki fark


Yanıtlar:


157

Farklılıklar, tarih / saat türleri için PostgreSQL belgelerinde ele alınmıştır . Evet, tedavisi TIMEveya TIMESTAMPbiri WITH TIME ZONEveyaWITHOUT TIME ZONE . Değerlerin nasıl saklandığını etkilemez; nasıl yorumlandıklarını etkiler.

Saat dilimlerinin bu veri türleri üzerindeki etkileri özellikle dokümanlarda yer almaktadır. Fark, sistemin değer hakkında makul bir şekilde bildikleri şeyden kaynaklanır:

  • Değerin bir parçası olarak bir saat dilimi ile, değer istemcide yerel bir saat olarak görüntülenebilir.

  • Değerin bir parçası olarak saat dilimi olmadan, varsayılan varsayılan saat dilimi UTC'dir, bu nedenle o saat dilimi için oluşturulur.

Davranış en az üç faktöre bağlı olarak değişir:

  • İstemcideki saat dilimi ayarı.
  • Değerin veri türü (yani WITH TIME ZONEveya WITHOUT TIME ZONE).
  • Değerin belirli bir saat dilimi ile belirtilip belirtilmediği.

İşte bu faktörlerin kombinasyonlarını kapsayan örnekler:

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)

88
Yalnızca değer ekleme / alma işlemine atıfta bulunursa düzeltin. Ancak okuyucular, her iki veri türünün de timestamp with time zoneve timestamp without time zonePostgres'te * saat dilimi bilgilerinin gerçekte saklanmadığını anlamalıdır . Bunu veri türü doc sayfasına bir bakışta doğrulayabilirsiniz: Her iki tür de aynı sayıda sekizli alır ve kaydedilen değer aralığına sahiptir, bu nedenle zaman dilimi bilgilerini depolamak için yer yoktur. Sayfanın metni bunu doğrular. Bir yanlış adlandırma: "tz olmadan", "veri eklenirken ofseti yoksay" ve "tz ile", UTC'ye ayarlamak için ofseti kullan "anlamına gelir.
Basil Bourque

42
Veri türleri ikinci bir yanlış adlandırmadır: "Saat dilimi" derler, ancak aslında UTC / GMT'den ofsetten bahsediyoruz. Bir saat dilimi aslında bir ofset artı Yaz Saati Uygulaması (DST) ve diğer anormallikler hakkında kurallar / geçmiştir.
Basil Bourque

4
Ben bir denge bir zaman dilimi artı DST için kurallar olduğunu söyleyebilirim. Bir ofset verilen saat dilimini bulamazsınız, ancak saat dilimi ve DST kurallarına göre ofseti keşfedebilirsiniz.
igorsantos07

3
Resmi dokümana atıfta bulunmak : Tüm saat dilimine uyan tarihler ve saatler dahili olarak UTC'de saklanır. İstemciye gösterilmeden önce TimeZone yapılandırma parametresi tarafından belirtilen bölgedeki yerel saate dönüştürülür.
Guillaume Husta

2
@ igorsantos07 Saat dilimi , DST değişiklikleri ve diğer değişikliklerle ilgili kurallar / geçmiş kümesidir. İfadeleriniz gereksiz görünüyor. Ve "bir ofset bir saat dilimi artı DST için kurallar" ifadesi basitçe yanlıştır: bir ofset sadece birkaç saat, dakika ve saniyedir - başka bir şey değil, daha az.
Basil Bourque

34

Söz konusu PostgreSQL belgelerinden daha anlaşılır bir şekilde açıklamaya çalışıyorum.

Her iki TIMESTAMPdeğişken de adların önerdiğine rağmen bir saat dilimi (veya bir uzaklık) depolamaz. Fark, depolama biçiminde değil, depolanan verilerin yorumlanmasında (ve amaçlanan uygulamada):

  • TIMESTAMP WITHOUT TIME ZONEyerel tarih saatini saklar (duvar takvimi tarihi ve duvar saati saati olarak da bilinir). Saat dilimi PostgreSQL'in anlayabileceği kadarıyla belirtilmemiş (uygulamanız ne olduğunu biliyor olsa da). Bu nedenle, PostgreSQL giriş veya çıkışta saat dilimi ile ilgili bir dönüştürme yapmaz. Değer veritabanına olarak girildiyse '2011-07-01 06:30:30', daha sonra hangi saat diliminde görüntülediğiniz hiçbir materyal yoksa, yine de 2011 yılı, 07, ay 01, 06 saat, 30 dakika ve 30 saniye (bazı formatlarda) der. Ayrıca, herhangi bir ofset veya giriş belirttiğiniz saat dilimi PostgreSQL tarafından göz ardı edilir, böylece '2011-07-01 06:30:30+00've '2011-07-01 06:30:30+05'sadece aynıdır '2011-07-01 06:30:30'. Java geliştiricileri için: buna benzer java.time.LocalDateTime.

  • TIMESTAMP WITH TIME ZONEUTC zaman çizgisinde bir nokta saklar. Görünüşü (kaç saat, dakika vb.) Saat diliminize bağlıdır, ancak her zaman aynı "fiziksel" anı ifade eder (gerçek bir fiziksel olayın anı gibi). Giriş dahili olarak UTC'ye dönüştürülür ve bu şekilde kaydedilir. Bunun için, girdinin ofseti bilinmelidir, bu nedenle girdi hiçbir kesin ofset veya zaman dilimi (zaman) içermediğinde '2011-07-01 06:30:30', PostgreSQL oturumunun geçerli saat diliminde olduğu varsayılır, aksi takdirde açıkça belirtilen ofset veya saat dilimi kullanılır. (olduğu gibi '2011-07-01 06:30:30+05'). Çıktı, PostgreSQL oturumunun geçerli saat dilimine dönüştürülmüş olarak görüntülenir. Java geliştiricileri için: java.time.Instant(ancak daha düşük çözünürlükle), ancak JDBC ve JPA 2 ile benzerdir .java.time.OffsetDateTimejava.util.Date veyajava.sql.Timestamp

Bazıları her iki TIMESTAMPvaryasyonun UTC tarih-saatini sakladığını söylüyor . Bir tür, ama bence bu şekilde koymak kafa karıştırıcı. UTC saat dilimi ile oluşturulan TIMESTAMP WITHOUT TIME ZONEa TIMESTAMP WITH TIME ZONE, yerel tarih saatindekilerle aynı yıl, ay, gün, saat, dakika, saniye ve mikrosaniye verir. Ancak, UTC yorumunun söylediği zaman çizgisindeki noktayı temsil etmek anlamına gelmez, sadece yerel tarih-saat alanlarının kodlanma şekli. (Gerçek zaman dilimi UTC olmadığından, zaman çizgisindeki bazı noktalar kümesi; ne olduğunu bilmiyoruz.)


A'yı almanın yanlış bir TIMESTAMP WITH TIME ZONEyanı yoktur Instant. Her ikisi de UTC'de zaman çizelgesinde bir noktayı temsil eder. Instantbence OffsetDateTime, daha fazla kendi kendini belgelediği için tercih edilir : A TIMESTAMP WITH TIME ZONEher zaman veritabanından UTC olarak alınır ve Instanther zaman UTC'de bulunur, bu nedenle doğal bir eşleşme olurken, bir OffsetDateTimebaşka ofsetleri taşıyabilir.
Basil Bourque

@BasilBourque Maalesef, geçerli JDBC belirtimi, JPA 2.2 belirtimi ve ayrıca PostgreSQL JDBC belgelerinde yalnızca OffsetDateTimeeşlenen Java türü olarak bahsedilmektedir . InstanceHala bir yerde gayri resmi olarak desteklenip desteklenmediğinden emin değilim .
ddekany

soru, gibi girdi belirtmek herhangi bir ofset '2011-07-01 06:30:30+00've '2011-07-01 06:30:30+05' yok sayılır ama yapmak mümkün insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);ve doğru utc dönüştürecektir. tarih saat dilimi olmadan zaman damgasıdır. Zaman damgası ile zaman damgasının ana değerinin ne olduğunu anlamaya çalışıyorum ve sorun yaşıyorum.
pk1m

@ pk1m Sorunları karmaşık hale getiriyorsunuz ::timestamptz. Bununla birlikte, dizeyi dönüştürürsünüz TIMESTAMP WITH TIME ZONEve daha sonra ne zaman dönüştürülürse WITHOUT TIME ZONE, oturum saat diliminizden (belki UTC) görüldüğü gibi o anın "duvar takvimi" gününü ve duvar saati saatini depolar. Yine de yalnızca belirtilmemiş ofseti olan (bölge yok) yerel bir zaman damgası olacaktır.
ddekany

ben python ile çalışıyorum ve ne zaman damgası farkında datettime nesne eklemek eklenir thats. Bana öyle geliyor ki zaman dilimi ile zaman damgası kullanmanın bir değeri var, ama zaman dilimlerini işlemek gerekli değil.
pk1m

12

İşte size yardımcı olacak bir örnek. Saat dilimine sahip bir zaman damganız varsa, bu zaman damgasını başka bir saat dilimine dönüştürebilirsiniz. Temel saat dilimine sahip değilseniz, doğru şekilde dönüştürülmez.

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

Çıktı:

-[ 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

5
İfadesi "doğru dönüştürülür olmayacak" basitçe doğru değildir. Ne timestampve ne timestamptzanlama geldiğini anlamalısınız . timestamptzsaatin (UTC) mutlak bir noktası anlamına gelirken timestamp, belirli bir saat diliminde saatin ne gösterdiğini gösterir. Böylece, timestamptzbir saat dilimine dönüştürürken New York'ta saatin bu mutlak noktada ne gösterdiğini soruyorsunuz ? oysa a'yı dönüştürürken , New York'ta saat x'i gösterdiğinde mutlak noktanın netimestamp olduğunu soruyorsunuz ?
fphilipe

AT TIME ZONEZaten anlamak bile yapı, kendi teaser bir beyin WITHvs WITHOUT TIME ZONEtürlerini. Bu yüzden onları açıklamak için ilginç bir seçim. (: ( AT TIME ZONEbir WITH TIME ZONEzaman damgasını bir WITHOUT TIME ZONEzaman damgasına dönüştürür ve tam tersi ... tam olarak açık değildir.)
ddekany

now()::timestamp AT TIME ZONE 'CST''CST' bölgesi için saatin hangi saatte yerel saatinizin şu anda gösterildiğini göstermediğiniz sürece mantıklı değil
Jasen
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.