PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

PostgreSQL błędna konwersja ze znacznika czasu bez strefy czasowej na znacznik czasu ze strefą czasową

Kluczowe rzeczy do zrozumienia

timestamp without time zone AT TIME ZONE reinterpretuje timestamp jako przebywający w tej strefie czasowej w celu przekonwertowania go na UTC .

timestamp with time zone AT TIME ZONE konwertuje timestamp do timestamp w określonej strefie czasowej.

PostgreSQL używa stref czasowych ISO-8601, które określają, że wschód od Greenwich jest dodatni... chyba że używasz specyfikatora strefy czasowej POSIX, w którym to przypadku jest on zgodny z POSIX. Następuje szaleństwo.

Dlaczego pierwszy daje nieoczekiwany wynik

Sygnatury czasowe i strefy czasowe w SQL są okropne. To:

select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';

interpretuje literał nieznanego typu '2011-12-30 00:30:00' jako timestamp without time zone , który Pg zakłada, że ​​znajduje się w lokalnej strefie czasowej, chyba że podano inaczej. Gdy używasz AT TIME ZONE , jest (zgodnie ze specyfikacją) ponownie zinterpretowany jako timestamp with time zone w strefie czasowej EST5EDT następnie jest przechowywany jako czas bezwzględny w UTC - więc jest konwertowany z EST5EDT do UTC, czyli przesunięcie strefy czasowej jest odejmowane . x - (-5) to x + 5 .

Ten znacznik czasu, dostosowany do przechowywania UTC, jest następnie dostosowywany do Twojego serwera TimeZone ustawienie wyświetlania, aby było wyświetlane w czasie lokalnym.

Jeśli zamiast tego chcesz powiedzieć „Mam ten znacznik czasu w czasie UTC i chcesz zobaczyć, jaki jest odpowiednik czasu lokalnego w EST5EDT”, jeśli chcesz być niezależny od ustawienia strefy czasowej serwera, musisz napisać coś takiego:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE 'EST5EDT';

To mówi „Podany znacznik czasu 2011-12-30 00:30:00, potraktuj go jako znacznik czasu w UTC podczas konwersji na znacznik czasu, a następnie przekonwertuj ten znacznik czasu na czas lokalny w EST5EDT".

Okropne, prawda? Chcę porozmawiać z firmą ktokolwiek zdecydował się na szaloną semantykę AT TIME ZONE - tak naprawdę powinno to być coś w stylu timestamp CONVERT FROM TIME ZONE '-5' i timestamptz CONVERT TO TIME ZONE '+5' . Ponadto timestamp with time zone powinien faktycznie mieć przy sobie swoją strefę czasową, nie być przechowywany w UTC i automatycznie konwertowany na czas lokalny.

Dlaczego drugi działa (o ile strefa czasowa =UTC)

Twoja oryginalna wersja „działa”:

select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';

będzie poprawne tylko wtedy, gdy strefa czasowa jest ustawiona na UTC, ponieważ rzutowanie tekstu na znacznik czasu zakłada strefę czasową, gdy nie jest ona określona.

Dlaczego trzeci działa

Dwa problemy znoszą się nawzajem.

Druga wersja, która wydaje się działać, jest niezależna od strefy czasowej, ale działa tylko dlatego, że dwa problemy same się znoszą. Po pierwsze, jak wyjaśniono powyżej, timestamp without time zone AT TIME ZONE reinterpretuje znacznik czasu jako znajdujący się w tej strefie czasowej do konwersji na znacznik czasu UTC; to skutecznie odejmuje przesunięcie strefy czasowej.

Jednak z powodów, których nie znam, PostgreSQL używa znaczników czasu z odwrotnym znakiem do tego, do czego jestem przyzwyczajony w większości miejsc. Zobacz dokumentację:

Inną kwestią, o której należy pamiętać, jest to, że w nazwach stref czasowych POSIX stosuje się dodatnie przesunięcia dla lokalizacji na zachód od Greenwich. Wszędzie indziej PostgreSQL przestrzega konwencji ISO-8601, zgodnie z którą dodatnie przesunięcia stref czasowych znajdują się na wschód od Greenwich.

Oznacza to, że EST5EDT to to samo co +5 , a nie -5 . Dlatego to działa:ponieważ odejmujesz przesunięcie tz, a nie dodajesz go, ale odejmujesz zanegowane przesunięcie!

To, czego potrzebujesz, aby to poprawić, to:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE '+5';



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Użyj wyjścia tekstowego z funkcji jako nowego zapytania

  2. Dziwne zachowanie w Postgresql

  3. Przełączanie awaryjne replikacji PostgreSQL 101

  4. Jak monitorować PostgreSQL działający w kontenerze Dockera:część druga

  5. Jak wydrukować wynik zapytania PostgreSQL w formacie CSV lub TSV z wiersza poleceń?