Czy wartości znaczników czasu są przechowywane inaczej w PostgreSQL, gdy typ danych jest WITH TIME ZONE
kontra WITHOUT TIME ZONE
? Czy różnice można zilustrować za pomocą prostych przypadków testowych?
postgresql
types
timestamp
timezone
Larsenal
źródło
źródło
Odpowiedzi:
Różnice omówiono w dokumentacji PostgreSQL dla typów daty / godziny . Tak, leczenie
TIME
lubTIMESTAMP
różni się między jednymWITH TIME ZONE
lubWITHOUT TIME ZONE
. Nie wpływa to na sposób przechowywania wartości; wpływa na sposób ich interpretacji.Wpływ stref czasowych na te typy danych jest szczegółowo opisany w dokumentach. Różnica wynika z tego, co system może rozsądnie wiedzieć o wartości:
Gdy strefa czasowa jest częścią wartości, wartość może być renderowana jako czas lokalny w kliencie.
Bez strefy czasowej jako części wartości oczywistą domyślną strefą czasową jest UTC, więc jest renderowana dla tej strefy czasowej.
Zachowanie różni się w zależności od co najmniej trzech czynników:
WITH TIME ZONE
LubWITHOUT TIME ZONE
) wartości.Oto przykłady obejmujące kombinacje tych czynników:
źródło
timestamp with time zone
atimestamp without time zone
w Postgres zrobić * nie faktycznie strefa czasowa sklep. Możesz to potwierdzić, rzut oka na stronę dokumentu typu danych: Oba typy zajmują tę samą liczbę oktetów i mają zapisany zakres wartości, więc nie ma miejsca na przechowywanie informacji o strefie czasowej. Tekst strony to potwierdza. Coś mylnego: „bez tz” oznacza „ignoruj przesunięcie przy wstawianiu danych”, a „z tz” oznacza „użyj przesunięcia, aby dostosować się do UTC”.Staram się wyjaśnić to bardziej zrozumiale niż przywołana dokumentacja PostgreSQL.
Żaden
TIMESTAMP
wariant nie przechowuje strefy czasowej (ani przesunięcia), pomimo sugerowanych nazw. Różnica polega na interpretacji przechowywanych danych (i zamierzonej aplikacji), a nie na samym formacie przechowywania:TIMESTAMP WITHOUT TIME ZONE
przechowuje lokalną datę i godzinę (aka. kalendarz ścienny i zegar ścienny). Jego strefa czasowa jest nieokreślona, o ile PostgreSQL może powiedzieć (chociaż twoja aplikacja może wiedzieć, co to jest). Dlatego PostgreSQL nie dokonuje konwersji związanej ze strefą czasową na wejściu lub wyjściu. Jeśli wartość została wprowadzona do bazy danych jako'2011-07-01 06:30:30'
, to bez względu na to, w której strefie czasowej wyświetlisz ją później, nadal będzie podawać rok 2011, miesiąc 07, dzień 01, 06 godzin, 30 minut i 30 sekund (w pewnym formacie). Ponadto, każdy przesunięcie strefy czasowej lub podać na wejściu jest ignorowany przez PostgreSQL, tak'2011-07-01 06:30:30+00'
i'2011-07-01 06:30:30+05'
są takie same jak po prostu'2011-07-01 06:30:30'
. Dla programistów Java: jest to analogiczne dojava.time.LocalDateTime
.TIMESTAMP WITH TIME ZONE
zapisuje punkt na linii czasu UTC. To, jak to wygląda (ile godzin, minut itp.) Zależy od twojej strefy czasowej, ale zawsze odnosi się do tej samej „fizycznej” chwili (jak moment rzeczywistego zdarzenia fizycznego). Dane wejściowe są wewnętrznie konwertowane na UTC i tak są przechowywane. W tym celu musi być znane przesunięcie wejścia, więc jeśli dane wejściowe nie zawierają wyraźnego przesunięcia lub strefy czasowej (jak'2011-07-01 06:30:30'
), zakłada się, że znajduje się w bieżącej strefie czasowej sesji PostgreSQL, w przeciwnym razie zostanie użyte jawnie określone przesunięcie lub strefa czasowa (jak w'2011-07-01 06:30:30+05'
). Dane wyjściowe są wyświetlane przekonwertowane na bieżącą strefę czasową sesji PostgreSQL. Dla programistów Java: Jest to analogiczne dojava.time.Instant
(z niższą rozdzielczością), ale w JDBC i JPA 2.2 należy go odwzorowaćjava.time.OffsetDateTime
(java.util.Date
lubjava.sql.Timestamp
oczywiście).Niektórzy twierdzą, że obie
TIMESTAMP
wersje przechowują datę i godzinę UTC. W pewnym sensie, ale moim zdaniem jest to mylące.TIMESTAMP WITHOUT TIME ZONE
jest przechowywany jakTIMESTAMP WITH TIME ZONE
, który renderowany ze strefą czasową UTC daje ten sam rok, miesiąc, dzień, godziny, minuty, sekundy i mikrosekundy, jak w lokalnej dacie i czasie. Ale to nie ma oznaczać punktu na linii czasu, który mówi interpretacja UTC, to po prostu sposób, w jaki są zakodowane lokalne pola daty i godziny. (To jakaś grupa kropek na linii czasu, ponieważ strefa czasu rzeczywistego to nie UTC; nie wiemy, co to jest.)źródło
TIMESTAMP WITH TIME ZONE
jakoInstant
. Oba stanowią punkt na osi czasu w UTC.Instant
jest, moim zdaniem,OffsetDateTime
bardziej preferowane niż bardziej samo-dokumentujące: ATIMESTAMP WITH TIME ZONE
jest zawsze pobierane z bazy danych jako UTC, iInstant
zawsze jest w UTC, więc jest naturalnym dopasowaniem, podczas gdyOffsetDateTime
może przenosić inne przesunięcia.OffsetDateTime
o typie zamapowanego Java. Nie jestem pewien, czyInstance
jest gdzieś nieoficjalnie obsługiwany.'2011-07-01 06:30:30+00'
i'2011-07-01 06:30:30+05'
jest ignorowane, ale jestem w stanie to zrobićinsert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
i poprawnie przekonwertuje to na utc. gdzie data jest datownikiem bez strefy czasowej. Próbuję zrozumieć, jaka jest główna wartość znacznika czasu w strefie czasowej i mam problemy.::timestamptz
. W ten sposób konwertujesz ciąg znakówTIMESTAMP WITH TIME ZONE
, a kiedy będzie on dalej konwertowanyWITHOUT TIME ZONE
, na którym będzie przechowywany „kalendarz ścienny” dzień i zegar ścienny tej chwili, jak widać ze strefy czasowej sesji (która może być UTC). Nadal będzie to tylko lokalny znacznik czasu z nieokreślonym przesunięciem (bez strefy).Oto przykład, który powinien pomóc. Jeśli masz znacznik czasu ze strefą czasową, możesz przekonwertować go na dowolną inną strefę czasową. Jeśli nie masz podstawowej strefy czasowej, nie zostanie poprawnie przekonwertowana.
Wynik:
źródło
timestamp
itimestamptz
znaczy.timestamptz
oznacza bezwzględny punkt w czasie (UTC), podczas gdytimestamp
oznacza to, co zegar pokazywał w określonej strefie czasowej. W związku z tym,timestamptz
przechodząc do strefy czasowej, pytasz, co zegar pokazywał w Nowym Jorku w tym absolutnym momencie? podczas gdy „konwertując” atimestamp
, pytasz, jaki był absolutny moment w czasie, gdy zegar w Nowym Jorku pokazywał x?AT TIME ZONE
Konstrukt to łamigłówka własną rękę, nawet jeśli już zrozumiećWITH
porównaniuWITHOUT TIME ZONE
typy. To ciekawy wybór, aby je wyjaśnić. (: (AT TIME ZONE
konwertujeWITH TIME ZONE
znacznik czasu naWITHOUT TIME ZONE
znacznik czasu i odwrotnie ... nie do końca oczywiste.)now()::timestamp AT TIME ZONE 'CST'
nie ma sensu, chyba że co, w jakim momencie zegar dla strefy „CST” wskazywałby czas, który aktualnie pokazuje twój lokalny zegar