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

Jaki typ znacznika czasu wybrać w bazie danych PostgreSQL?

Po pierwsze, obsługa czasu i arytmetyka PostgreSQL jest fantastyczna, a opcja 3 jest w porządku w ogólnym przypadku. Jest to jednak niekompletny obraz czasu i stref czasowych i można go uzupełnić:

  1. Zapisz nazwę strefy czasowej użytkownika jako preferencję użytkownika (np. America/Los_Angeles , a nie -0700 ).
  2. Przesyłanie danych o zdarzeniach/czasach użytkownika lokalnie w ich układzie odniesienia (najprawdopodobniej przesunięcie względem czasu UTC, takie jak -0700 ).
  3. W aplikacji przekonwertuj czas na UTC i przechowywane przy użyciu TIMESTAMP WITH TIME ZONE kolumna.
  4. Żądania zwrotu czasu lokalnego na strefę czasową użytkownika (tj. konwersja z UTC do America/Los_Angeles ).
  5. Ustaw timezone bazy danych do UTC .

Ta opcja nie zawsze działa, ponieważ ustalenie strefy czasowej użytkownika może być trudne, dlatego zaleca się użycie TIMESTAMP WITH TIME ZONE do lekkich zastosowań. To powiedziawszy, pozwól mi bardziej szczegółowo wyjaśnić niektóre aspekty tej Opcji 4.

Podobnie jak w przypadku opcji 3, powód WITH TIME ZONE dzieje się tak, ponieważ czas, w którym coś się wydarzyło, jest absolutny moment w czasie. WITHOUT TIME ZONE daje krewny strefa czasowa. Nigdy, przenigdy nie mieszaj bezwzględnych i względnych ZNACZNIKÓW CZASU.

Z perspektywy programistycznej i spójności upewnij się, że wszystkie obliczenia są wykonywane przy użyciu czasu UTC jako strefy czasowej. Nie jest to wymóg PostgreSQL, ale pomaga przy integracji z innymi językami programowania lub środowiskami. Ustawianie CHECK na kolumnie, aby upewnić się, że zapis do kolumny znacznika czasu ma przesunięcie strefy czasowej o wartości 0 to pozycja obronna, która zapobiega kilku klasom błędów (np. skrypt zrzuca dane do pliku, a coś innego sortuje dane czasowe za pomocą sortowania leksykalnego). Ponownie, PostgreSQL nie potrzebuje tego do poprawnego obliczania dat lub konwersji między strefami czasowymi (tj. PostgreSQL jest bardzo biegły w konwersji czasu między dowolnymi dwiema dowolnymi strefami czasowymi). Aby upewnić się, że dane wchodzące do bazy danych są przechowywane z przesunięciem równym zero:

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

Nie jest w 100% doskonały, ale zapewnia wystarczająco silny środek zapobiegający strzelaniu do stóp, który zapewnia, że ​​dane są już przekonwertowane na UTC. Istnieje wiele opinii na temat tego, jak to zrobić, ale z mojego doświadczenia wynika, że ​​jest to najlepsza praktyka.

Krytyka obsługi stref czasowych bazy danych jest w dużej mierze uzasadniona (jest wiele baz danych, które radzą sobie z tym z dużą niekompetencją), jednak obsługa znaczników czasu i stref czasowych przez PostgreSQL jest całkiem niesamowita (pomimo kilku „funkcji” tu i tam). Na przykład jedna z takich funkcji:

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

Pamiętaj, że AT TIME ZONE 'UTC' usuwa informacje o strefie czasowej i tworzy względny TIMESTAMP WITHOUT TIME ZONE używając układu odniesienia celu (UTC ).

Podczas konwersji z niekompletnego TIMESTAMP WITHOUT TIME ZONE do TIMESTAMP WITH TIME ZONE , brakująca strefa czasowa jest dziedziczona z Twojego połączenia:

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)

Dolna linia:

  • zapisz strefę czasową użytkownika jako nazwaną etykietę (np. America/Los_Angeles ), a nie przesunięcie względem czasu UTC (np. -0700 )
  • użyj czasu UTC do wszystkiego, chyba że istnieje ważny powód, aby przechowywać niezerowe przesunięcie
  • traktuj wszystkie niezerowe czasy UTC jako błąd wejściowy
  • nigdy nie mieszaj i nie dopasowuj względnych i bezwzględnych znaczników czasu
  • użyj także UTC jako timezone w bazie danych, jeśli to możliwe

Uwaga dotycząca losowego języka programowania:datetime Pythona typ danych jest bardzo dobry w utrzymywaniu rozróżnienia między czasami bezwzględnymi a względnymi (choć początkowo frustrujące, dopóki nie uzupełnisz go biblioteką taką jak PyTZ).

EDYTUJ

Pozwólcie, że wyjaśnię nieco więcej różnicy między wartościami względnymi a absolutnymi.

Do rejestracji zdarzenia używany jest czas bezwzględny. Przykłady:„Zalogowany użytkownik 123” lub „ceremonia wręczenia dyplomów rozpoczyna się 28.05.2011 o 14:00 czasu PST”. Bez względu na lokalną strefę czasową, gdybyś mógł teleportować się do miejsca, w którym miało miejsce zdarzenie, mógłbyś być jego świadkiem. Większość danych czasowych w bazie danych jest bezwzględna (i dlatego powinna być TIMESTAMP WITH TIME ZONE , najlepiej z przesunięciem +0 i etykietą tekstową reprezentującą zasady rządzące konkretną strefą czasową - nie przesunięcie).

Względnym wydarzeniem byłoby nagranie lub zaplanowanie czasu czegoś z perspektywy strefy czasowej, która jeszcze nie została określona. Przykłady:„drzwi naszej firmy otwierają się o 8 rano i zamykają o 21:00”, „spotkajmy się w każdy poniedziałek o 7 rano na cotygodniowym spotkaniu śniadaniowym” lub „w każde Halloween o 20:00”. Ogólnie rzecz biorąc, czas względny jest używany w szablonie lub fabryce dla zdarzeń, a czas bezwzględny jest używany dla prawie wszystkiego innego. Jest jeden rzadki wyjątek, na który warto zwrócić uwagę, który powinien zilustrować wartość czasów względnych. W przypadku przyszłych zdarzeń, które są wystarczająco odległe w przyszłości, w których może istnieć niepewność co do bezwzględnego czasu, w którym coś może się wydarzyć, użyj względnego znacznika czasu. Oto przykład z prawdziwego świata:

Załóżmy, że jest rok 2004 i musisz zaplanować dostawę 31 października 2008 r. o godzinie 13:00 na zachodnim wybrzeżu Stanów Zjednoczonych (tj. America/Los_Angeles /PST8PDT ). Jeśli zapisałeś to przy użyciu czasu bezwzględnego za pomocą ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE , dostawa pojawiłaby się o godzinie 14.00, ponieważ rząd Stanów Zjednoczonych uchwalił ustawę o polityce energetycznej z 2005 r., która zmieniła zasady regulujące czas letni. W 2004 roku, kiedy zaplanowano dostawę, data 10-31-2008 byłby czas pacyficzny standardowy (+8000 ), ale począwszy od roku 2005+ bazy danych stref czasowych rozpoznały, że 10-31-2008 byłby czas letni na Pacyfiku (+0700 ). Przechowywanie względnego znacznika czasu ze strefą czasową skutkowałoby prawidłowym harmonogramem dostaw, ponieważ względny znacznik czasu jest odporny na manipulacje ze strony Kongresu ze strony niewłaściwych informacji. Gdzie granica między używaniem względnych i bezwzględnych czasów do planowania jest rozmyta, ale moją praktyczną zasadą jest to, że planowanie czegokolwiek w przyszłości dalej niż 3-6 miesięcy powinno korzystać ze względnych znaczników czasu (zaplanowane =bezwzględne vs planowane =względny ???).

Innym/ostatnim typem czasu względnego jest INTERVAL . Przykład:„sesja wygaśnie 20 minut po zalogowaniu się użytkownika”. INTERVAL mogą być używane poprawnie z bezwzględnymi znacznikami czasu (TIMESTAMP WITH TIME ZONE ) lub względne znaczniki czasu (TIMESTAMP WITHOUT TIME ZONE ). Równie poprawne jest powiedzenie „sesja użytkownika wygasa po 20 minutach od pomyślnego zalogowania (login_utc + session_duration)” lub „nasze poranne spotkanie przy śniadaniu może trwać tylko 60 minut (recurring_start_time + meeting_length)”.

Ostatnie zamieszanie:DATE , TIME , TIME WITHOUT TIME ZONE i TIME WITH TIME ZONE to wszystkie względne typy danych. Na przykład:'2011-05-28'::DATE reprezentuje datę względną, ponieważ nie masz informacji o strefie czasowej, które mogłyby posłużyć do zidentyfikowania północy. Podobnie '23:23:59'::TIME jest względne, ponieważ nie znasz strefy czasowej ani DATE reprezentowany przez czas. Nawet z '23:59:59-07'::TIME WITH TIME ZONE , nie wiesz, jaka jest DATE byłoby. I na koniec DATE ze strefą czasową nie jest w rzeczywistości DATE , jest to 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)

Umieszczanie dat i stref czasowych w bazach danych to dobra rzecz, ale łatwo jest uzyskać nieco niepoprawne wyniki. Prawidłowe i pełne przechowywanie informacji o czasie wymaga minimalnego dodatkowego wysiłku, jednak nie oznacza to, że dodatkowy wysiłek jest zawsze wymagany.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak dopasować cały dzień do pola daty i godziny?

  2. Obliczanie sumy skumulowanej w PostgreSQL

  3. Docker czeka na uruchomienie postgresql

  4. Klucze podstawowe z Apache Spark

  5. Śledzenie wysokiej dostępności PostgreSQL za pomocą funkcji Heartbeat