Odpowiedzi:
Różnice omówiono w dokumentacji PostgreSQL dla typów daty / godziny . Tak, leczenie TIME
lub TIMESTAMP
różni się między jednym WITH 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
Lub WITHOUT TIME ZONE
) wartości.Oto przykłady obejmujące kombinacje tych czynników:
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)
timestamp with time zone
a timestamp 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 do java.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 do java.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 jak TIMESTAMP 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.)
TIMESTAMP WITH TIME ZONE
jako Instant
. Oba stanowią punkt na osi czasu w UTC. Instant
jest, moim zdaniem, OffsetDateTime
bardziej preferowane niż bardziej samo-dokumentujące: A TIMESTAMP WITH TIME ZONE
jest zawsze pobierane z bazy danych jako UTC, i Instant
zawsze jest w UTC, więc jest naturalnym dopasowaniem, podczas gdy OffsetDateTime
może przenosić inne przesunięcia.
OffsetDateTime
o typie zamapowanego Java. Nie jestem pewien, czy Instance
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ów TIMESTAMP WITH TIME ZONE
, a kiedy będzie on dalej konwertowany WITHOUT 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.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Wynik:
-[ 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
timestamp
i timestamptz
znaczy. timestamptz
oznacza bezwzględny punkt w czasie (UTC), podczas gdy timestamp
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” a timestamp
, 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ównaniu WITHOUT TIME ZONE
typy. To ciekawy wybór, aby je wyjaśnić. (: ( AT TIME ZONE
konwertuje WITH TIME ZONE
znacznik czasu na WITHOUT 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