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

Całkowite ignorowanie stref czasowych w Rails i PostgreSQL

Postgres ma dwa różne typy danych znaczników czasu:

  • timestamp with time zone , krótka nazwa:timestamptz
  • timestamp without time zone , krótka nazwa:timestamptz

timestamptz jest preferowany wpisz w rodzinie daty/godziny, dosłownie. Ma typispreferred ustaw w pg_type , co może mieć znaczenie:

  • Generowanie szeregów czasowych między dwiema datami w PostgreSQL

Pamięć wewnętrzna i epoka

Wewnętrznie sygnatury czasowe zajmują 8 bajtów miejsca na dysku iw pamięci RAM. Jest to liczba całkowita reprezentująca liczbę mikrosekund z epoki Postgres, 2000-01-01 00:00:00 UTC.

Postgres ma również wbudowaną wiedzę na temat powszechnie używanego liczenia sekund czasu UNIX z epoki UNIX, 1970-01-01 00:00:00 UTC, i używa jej w funkcjach to_timestamp(double precision) lub EXTRACT(EPOCH FROM timestamptz) .

Kod źródłowy:

* Timestamps, as well as the h/m/s fields of intervals, are stored as
* int64 values with units of microseconds.  (Once upon a time they were  
* double values with units of seconds.)

Oraz:

/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */  
#define UNIX_EPOCH_JDATE        2440588 /* == date2j(1970, 1, 1) */  
#define POSTGRES_EPOCH_JDATE    2451545 /* == date2j(2000, 1, 1) */  

Rozdzielczość w mikrosekundach przekłada się na maksymalnie 6 cyfr ułamkowych dla sekund.

timestamptz

Dla timestamp nie podano wyraźnie strefy czasowej. Postgres ignoruje dowolny modyfikator strefy czasowej dodany przez pomyłkę do literału wejściowego!

Żadne godziny nie są przesuwane do wyświetlania. Ponieważ wszystko dzieje się w tej samej strefie czasowej, jest to w porządku. Dla innej strefy czasowej znaczenie zmiany, ale wartość i wyświetlacz pozostań taki sam.

timestamptz

Obsługa timestamp jest subtelnie inny. Cytuję instrukcję tutaj:

Dla timestamp with time zone , wewnętrznie przechowywana wartość jest zawsze w UTC (Uniwersalny czas koordynowany...)

Moje odważne podkreślenie. Sama strefa czasowa nigdy nie jest przechowywana . Jest to modyfikator wejściowy używany do obliczania odpowiedniego znacznika czasu UTC, który jest przechowywany - lub dekorator wyjściowy używany do obliczania czasu lokalnego do wyświetlania - z dołączonym przesunięciem strefy czasowej. Jeśli nie dołączysz przesunięcia dla timestamptz na wejściu przyjmuje się bieżące ustawienie strefy czasowej sesji. Wszystkie obliczenia są wykonywane z wartościami znaczników czasu UTC. Jeśli (możesz) mieć do czynienia z więcej niż jedną strefą czasową, użyj timestamptz . Innymi słowy:jeśli istnieją jakiekolwiek wątpliwości lub nieporozumienia dotyczące zakładanej strefy czasowej, skorzystaj z timestamptz . Ma zastosowanie w większości przypadków użycia.

Klienty takie jak psql lub pgAdmin lub dowolna aplikacja komunikująca się przez libpq (np. Ruby z gem pg) są przedstawiane ze znacznikiem czasu plus przesunięcie dla bieżącej strefy czasowej lub zgodnie z wnioskiem strefa czasowa (patrz poniżej). Zawsze jest ten sam punkt w czasie , zmienia się tylko format wyświetlania. Lub, jak mówi instrukcja:

Wszystkie daty i godziny uwzględniające strefę czasową są przechowywane wewnętrznie w UTC. Są konwertowane na czas lokalny w strefie określonej przez TimeZone parametr konfiguracyjny przed wyświetleniem na kliencie.

Przykład w psql:

db=# SELECT timestamptz '2012-03-05 20:00+03';
      timestamptz
------------------------
 2012-03-05 18:00:00+01

Co się tutaj stało?
Wybrałem dowolne przesunięcie strefy czasowej +3 dla literału wejściowego. Dla Postgresa jest to tylko jeden z wielu sposobów wprowadzania znacznika czasu UTC 2012-03-05 17:00:00 . Wynik zapytania jest wyświetlany dla aktualnego ustawienia strefy czasowej Wiedeń/Austria w moim teście, który ma przesunięcie +1 zimą i +2 w okresie letnim („czas letni”, DST). Więc 2012-03-05 18:00:00+01 ponieważ czas letni zaczyna się dopiero później.

Postgres natychmiast zapomina o literale wejściowym. Zapamiętuje tylko wartość typu danych. Podobnie jak w przypadku liczby dziesiętnej. numeric '003.4' lub numeric '+3.4' - oba dają dokładnie taką samą wartość wewnętrzną.

AT TIME ZONE

Jedyne, czego teraz brakuje, to narzędzie do interpretacji lub reprezentowania literałów znaczników czasu zgodnie z określoną strefą czasową. To tam AT TIME ZONE pojawia się konstrukcja. Istnieją dwa różne przypadki użycia. timestamptz jest konwertowany na timestamp i odwrotnie.

Aby wprowadzić timestamp UTC 2012-03-05 17:00:00+0 :

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'

... co jest równoznaczne z:

SELECT timestamptz '2012-03-05 17:00:00 UTC'

Aby wyświetlić ten sam punkt w czasie, co EST timestamp (Czas wschodni standardowy):

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'

Zgadza się, AT TIME ZONE 'UTC' dwa razy . Pierwszy interpretuje timestamp wartość jako (podany) znacznik czasu UTC zwracający typ timestamptz . Drugi konwertuje timestamp do timestamp w danej strefie czasowej 'EST' - co zegar ścienny wyświetla w strefie czasowej EST w tym momencie.

Przykłady

SELECT ts AT TIME ZONE 'UTC'
FROM  (
   VALUES
      (1, timestamptz '2012-03-05 17:00:00+0')
    , (2, timestamptz '2012-03-05 18:00:00+1')
    , (3, timestamptz '2012-03-05 17:00:00 UTC')
    , (4, timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') 
    , (5, timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') 
    , (6, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'US/Hawaii')  -- ①
    , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii')                  -- ①
    , (8, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'HST')        -- ①
    , (9, timestamp   '2012-03-05 18:00:00+1')  -- ② loaded footgun!
      ) t(id, ts);

Zwraca 8 (lub 9) identyczne wiersze z kolumnami sygnatury czasowej o tej samej sygnaturze czasowej UTC 2012-03-05 17:00:00 . Dziewiąty rząd działa w mojej strefie czasowej, ale jest złą pułapką. Zobacz poniżej.

① Wiersze 6-8 ze strefą czasową nazwa i strefa czasowa skrót czas na Hawajach podlega DST (czasowi letniemu) i może się różnić, choć obecnie nie. Nazwa strefy czasowej, np. 'US/Hawaii' jest świadomy reguł czasu letniego i wszystkich historycznych zmian automatycznie, podczas gdy skrót taki jak HST to tylko głupi kod dla stałego przesunięcia. Może być konieczne dodanie innego skrótu dla czasu letniego/standardowego. Nazwa poprawnie interpretuje dowolne znacznik czasu w podanej strefie czasowej. skrót jest tani, ale musi być odpowiedni dla podanej sygnatury czasowej:

  • Nazwy stref czasowych z identycznymi właściwościami dają różne wyniki po zastosowaniu do znacznika czasu

Czas letni nie należy do najjaśniejszych pomysłów, jakie kiedykolwiek wpadła ludzkość.

② Wiersz 9, oznaczony jako załadowany footgun działa dla mnie , ale tylko przez przypadek. Jeśli jawnie rzutujesz literał na timestamp [without time zone] , każde przesunięcie strefy czasowej jest ignorowane! Używany jest tylko sam znacznik czasu. Wartość jest następnie automatycznie zmieniana na timestamptz w przykładzie, aby dopasować typ kolumny. W tym kroku timezone zakładane jest ustawienie bieżącej sesji, która jest tą samą strefą czasową +1 w moim przypadku (Europa/Wiedeń). Ale prawdopodobnie nie w twoim przypadku - co spowoduje inną wartość. W skrócie:nie przesyłaj timestamptz literały do ​​timestamp lub stracisz przesunięcie strefy czasowej.

Twoje pytania

Użytkownik zapisuje czas, powiedzmy 17 marca 2012 o 19:00. Nie chcę, aby konwersje stref czasowych lub strefa czasowa były przechowywane.

Sama strefa czasowa nigdy nie jest przechowywana. Użyj jednej z powyższych metod, aby wprowadzić znacznik czasu UTC.

Używam tylko strefy czasowej określonej przez użytkownika, aby uzyskać rekordy „przed” lub „po” bieżącym czasie w lokalnej strefie czasowej użytkownika.

Możesz użyć jednego zapytania dla wszystkich klientów w różnych strefach czasowych.
Dla bezwzględnego czasu globalnego:

SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time

Czas według lokalnego zegara:

SELECT * FROM tbl WHERE time_col > now()::time

Nie masz jeszcze dość podstawowych informacji? Więcej informacji znajdziesz w instrukcji.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Grupuj według interwałów danych

  2. Uzyskaj identyfikator z warunkowego INSERT

  3. Mierzenie statystyk punktów kontrolnych PostgreSQL

  4. ClusterControl — zaawansowane zarządzanie kopiami zapasowymi — PostgreSQL

  5. Szkolenie PostgreSQL dla MySQLerów