Postgres - daty

Postgres posiada cały zestaw typów do pracy z datami:
- timestamp without time zone (równoważny typowi timestamp; bez strefy czasowej) oraz timestamp with time zone
- date
- time without time zone (równoważny typowi time bez strefy czasowej) oraz time with timezone
- interval
Typy
Wartości tych typów:
- zajmują na nośniku różną ilość miejsca
- mają różne zakresy (typ date ma rozdzielczość jednego dnia, a pozostałe - jednej mikrosekundy)
- mają też oczywiście inne znaczenie.
Listę typow w postgresie przedstawia poniższa tabelka:
Opcjonalny parametr (p) może przyjąć wartość od 0 do 6 i oznacza liczbę cyfr dziesiętnych w polu sekund (domyślnie nie jest nałożone żadne ograniczenie).
|
|
Typ interval może przyjąć parametr fields i może to być jedna z poniższych wartości:
|
|
Na przykład różnica czasu między dwiema datami wyrażona jest wartością typu interval:
|
|
Zastosowania
- do reprezentowania dat najlepiej użyć typu date
- do prezyzyjnego określenia czasu - jednego z wariantów typu time
- do określenia momentu zajścia zdarzenia tradycyjnie używa się wariantu timestamp
- natomiast w celu zapisania czasu trwania (bez konkretnych dat/czasów początku i końca) - typu interval
Wejście
Wprowadzanie dat
PostgreSQL jest bardzo liberalny jeśli chodzi o akceptowanie danych dat/czasu na “wejściu”. Akceptuje:
- daty w standardie ISO 8601
- daty w formatach YMD (year, month, day) i MDY (month, day, year), a także
- daty w kalendarzu juliańskim
W ogólności literały “czasowe” mają postać:
typ [(p)] ‘wartość’
a więc wartość literału jest zapisywana w pojedynczym apostrofie; na przykład:
|
|
Wprowadzanie czasu
Typy czasowe mają opcjonalnie dodaną strefę czasową, która może być wyrażona jako przesunięcie względem UTC, jako skrót oraz jako pełna nazwa strefy:
|
|
Wprowadzanie znaczników czasu
Literał warości timestamp ma postać napisu z datą, czasem, opcjonalnym przesunięciem oraz opcjonalnym znacznikiem AD lub BC. Należy użyć +
lub -
do oddzielenia timstampa i strefy, np.:
|
|
PostgreSQL określa typ na podstawie zadeklarowanego typu i nie parsuje w tym celu literału. Dlatego zapis:
|
|
zostanie uznany za timestamp without time zone.
Standard wymaga, aby poniższy literał otrzymał typ timestamp without time zone:
TIMESTAMP ‘2004-10-19 10:23:54’
Tu Postgres zachowa się zgodnie ze standardem.
Ten literał natomiast powinien według standardu mieć typ timestamp with time zone:
TIMESTAMP ‘2004-10-19 10:23:54+02’
Tu zachowanie postgresa jest niestandardowe - powstanie wartość typu timestamp without time zone.
Specjalne literały czasu
Jako literału czasu można również użyć (w pojedynczych apostrofach) następujących wartości:
Wprowadzanie interwałów
Interwały mogą być wprowadzane na dwa sposoby:
- W postaci niestandardowej mają postać
[@] quantity unit [quantity unit…] [direction] gdzie
- opcjonalny znak @ nic nie oznacza
- quantity to liczba ze znakiem
- unit to jedna z podanych wartości (w liczbie pojedynczej lub mnogiej w jęz. ang.): microsecond, millisecond, second, minute, hour, day, week, month, year, decade, century, millennium
przy czym dni, godziny, minuty i sekundy mogą być określone bez znaczników jednostek, np.
- ‘200-10’ to to samo co ‘200 years 10 months’
- ‘1 12:59:10’ to to samo, co ‘1 day 12 hours 59 min 10 sec’
- W wersji standardowej (ISO 8601) mają postać:
P quantity unit [ quantity unit …] [ T [ quantity unit …]]
lub alternatywnie:
P [ years-months-days ] [ T hours:minutes:seconds ]
przy czym początkowe P jest obowiązkowe, a części mniejsze od dnia muszą pojawić się po (wymaganym wówczas) T.
Na przykład:
- P1Y2M3DT4H5M6S
- P0001-02-03T04:05:06
Znaczniki jednostek możliwe do użycia (ISO 8601): Y Years M Months (in the date part) W Weeks D Days H Hours M Minutes (in the time part) S Seconds
Wyjście - wypisywanie dat
Daty/czas wg standardu SQL muszą być wypisywane w formacie ISO 8601. PostgreSQL czyni jedno odstępstwo od standardu: zamiast litery T oddzielającej datę od czasu używa on (zgodnie z zaleceniem RFC3339 spacji. Poza tym dopuszcza również inne sposoby wypisywania:
- ISO (ISO 8601) SQL standard, np. 1997-12-17 07:37:16-08
- SQL (tradycyjny), np. 12/17/1997 07:37:16.00 PST
- Postgres (oryginalny), np. Wed Dec 17 07:37:16 1997 PST
- German (regionalny), np. 17.12.1997 07:37:16.00 PST
Do bardziej precyzyjnego formatowania czasu służą funkcje formatujące, do których należą m.in.:
- to_char ( timestamp, text ) → text - konwertuje timestamp/czas do napisu wg podanego formatu:
- to_char(timestamp ‘2002-04-20 17:31:12.66’, ‘HH12:MI:SS’) → 05:31:12
- to_date ( text, text ) → date - konwertuje/parsuje napis do daty wg podanegp formatu, np.
- to_date(‘05 Dec 2000’, ‘DD Mon YYYY’) → 2000-12-05
Wzorce służące do budowania formatów można znaleźć w dokumentacji
Strefy czasowe
Życie na prawie kulistej Ziemi, na której doba trwa około 24 godziny, podzielonej na państwa, którym czasami udaje się dogadać w sprawie posługiwania się wspólnym standardem czasu… jest, cóż, skomplikowane. Zobaczmy, na czym polegają komplikacje.
Strefy czasowe zależą nie tylko od kształtu Ziemi, lecz także od decyzji politycznych. Na początku 19. wieku udało się wiele aspektów ustandaryzować, ale wprowadzenie okresów DST (daylight saving time - “oszczędzania dzięki światłu słonecznemu”) ponownie skomplikowało sposoby obliczania czasu.
Reguły określania stref
- Dla dat w przeszłości PostgreSQL używa reguł określania stref z bazy danych IANA (Olson)
- Dla dat w przyszłości ekstrapoluje w nieskończoność istniejące obecnie reguły (czyli zupełnie nierealistycznie zakłada, że nie zmienią się one w ogóle).
Sposoby określania strefy
Istnieją trzy sposoby określania strefy czasowej:
- pełna nazwa strefy (występująca w tabeli pg_timezone_names), np. Europe/Warsaw - nazwa ta pochodzi z bazy danych IANA i jest szeroko używana i rozpoznawana przez różne systemy informatyczne (patrz tabela
pg_timezone_names
) - skrót nazwy strefy, np. CET; ta specyfikacja oznacza (i określa) jedynie bezwzględne przesunięcie względem strefy UTC (podczas gdy pełna nazwa strefy czasowej może oznaczać i zwykle oznacza również przesunięcie wynikające z przyjętego okresu DST) (patrz tabela
pg_timezone_abbrevs
) - nazwy stref zgodne ze standardem POSIX Patrz Tabele
Pełna nazwa uwzględnia DST
Różnica między określeniem strefy skrótem a pełną nazwą może mieć istotne znaczenie. Jeśli weźmiemy pod uwagę, że skrót oznacza jedynie przesunięcie, a pełna nazwa bierze pod uwagę DST, to wtedy, na przykład, data w formacie: timestamptz ‘2021-10-31 02:00 Europe/Warsaw’ oznacza drugą w nocy (tuż po zmianie czasu) w strefie czasowej/geograficznej CET i jest to ten sam moment w czasie, co timestamptz ‘2021-10-31 02:00 CET’:
|
|
Jak wiadomo, w tym momencie (‘2021-10-31 02:00 CET’ to ‘2021-10-31 01:00 UTC’) przeszliśmy z czasu letniego (UTC+2) na czas zimowy (UTC+1) i przesunęliśmy zegarki w tył z godziny 3:00 (kiedy to byliśmy jeszcze w DST - w czasie letnim) na 2:00, co oznacza że mogliśmy rano dłużej pospać.
Ta więc godzinę i minutę wcześniej też była prawie druga w nocy - wskazanie lokalnego zegara pokazywało prawie tę samą godzinę: timestamptz ‘2021-10-31 01:59 Europe/Warsaw’, jednak w stosunku do strefy CET byliśmy przesunięci o godzinę:
|
|
O 01:59 geograficzna strefa Europe/Warsaw była wtedy jeszcze w strefie czasowej CEST (CEST = UTC+2) czyli w DST - przesunięta o dwie godziny wzgedem UTC (a jedną względem CET = UTC+1).
Patrz:
- strefy czasowe w Europie
- Rozporządzenie Prezesa Rady Ministrów z dnia 3 listopada 2016 r. w sprawie wprowadzenia i odwołania czasu letniego środkowoeuropejskiego w latach 2017–2021
- dyskusja tutaj i jeszcze tutaj
Tabele z nazwami stref czasowych
Rzućmy okiem na tabele z nazwami i skrótami stref:
- pg_timezone_names
- pg_timezone_abbrevs
Tabela z nazwami ma taką strukturę:
|
|
Natomiast tabela ze skrótami taką:
|
|
Polska - strefa czasowa
|
|
Z powyższych tabel mogę się dowiedzieć, że:
- skrót strefy czasowa dla Polski to CET (Central Europe Time)
- nazwa strefy to posix/Poland,Poland, Europe/Warsaw lub posix/Europe/Warsaw
- Polska nie jest w czasie “daylight saving time” (wartość
f
w kolumnie is_dst)
Ciekawe, czy gdybym sprawdziła tabelę pg_timezone_names dwa tygodnie temu, to czy zobaczyłabym inne wartości? Takie jak:
|
|
Funkcje
Lista funkcji do pracy z typami daty/czasu jest naprawdę imponująca. Warto te funkcje przejrzeć, by mieć orientację w możliwościach postgresa.
Do tworzenia czasów
- age ( timestamp, timestamp ) → interval - różnica czasów
- current_date → date - bieżąca data
- current_timestamp → timestamp with time zone - bieżący timestamptz (zmienna)
- now ( ) → timestamp with time zone - bieżący timestamptz (funkcja)
- make_date ( year int, month int, day int ) → date - tworzenie dat z liczb
- to_timestamp ( double precision ) → timestamp with time zone - Unix timestamp do timestamptx
Do sprawdzania nakładania się interwałów
Istnieje również interesujący operator logiczny (zwracający true
lub false
) OVERLAPS
operujący na parach: (start1, end1) OVERLAPS (start2, end2), np:
- SELECT (DATE ‘2001-02-16’, INTERVAL ‘100 days’) OVERLAPS (DATE ‘2001-10-30’, DATE ‘2002-10-30’);
Do wyciągania szczegółów
A także różne warianty EXTRACT (something FROM something)
, np.
- SELECT EXTRACT(QUARTER FROM TIMESTAMP ‘2001-02-16 20:38:40’); → 1
- SELECT EXTRACT(DAY FROM TIMESTAMP ‘2001-02-16 20:38:40’); → 16
Do przekształcania stref czasowych
Operator AT TIME ZONE przekształca
- czas bez strefy do określonej strefy: timestamp ‘2001-02-16 20:38:40’ at time zone ‘America/Denver’ → 2001-02-17 03:38:40+00
- a także czas ze strefą do innej strefy: time with time zone ‘05:34:17-05’ at time zone ‘UTC’ → 10:34:17+00
Podsumowanie
W tym wpisie:
- Przedstawilam krótki przegląd typów dostępnych w systemie PostgreSQL, a także omówiłam, w jaki sposób można wprowadzać wartości tych typów do systemu i jak je wypisać.
- Sprawdziłam, w jakich tabelach systemowych znajdują się skróty nazw oraz same nazwy stref czasowych.
- Nauczyłam się, że przy określeniu znacznika czasu ze strefą czasową (wartości typu
timestamptz
) warto wiedzieć, czy mamy do czynienia z czasem letnim czy zimpwym. - Spojrzałam na listę funkcji związanych z czasem i przedstawiłam kilka z nich.
Mam nadzieję, że nie tylko ja nauczyłam się czegoś nowego :)
Aktualizacja:
Artykuły na temat typów danych w Postgresie: tapoueh.org: data types
Ten wpis jest częścią serii sql.
- 2021-04-11 - Postgres - daty
- 2021-16-10 - Postgres - krótka analiza danych dot. zajęć dodatkowych
- 2021-04-10 - Postgres - JSON
- 2021-04-10 - Postgres - ćwiczenia
- 2021-30-09 - Postgres - instalacja