Używanie aktualnego czasu UTC jako wartości domyślnej w PostgreSQL

172

Mam kolumnę tego TIMESTAMP WITHOUT TIME ZONEtypu i chciałbym mieć domyślną aktualną godzinę w UTC. Uzyskanie aktualnego czasu w UTC jest łatwe:

postgres=# select now() at time zone 'utc';
          timezone          
----------------------------
 2013-05-17 12:52:51.337466
(1 row)

Tak jak przy użyciu aktualnego znacznika czasu dla kolumny:

postgres=# create temporary table test(id int, ts timestamp without time zone default current_timestamp);
CREATE TABLE
postgres=# insert into test values (1) returning ts;
             ts             
----------------------------
 2013-05-17 14:54:33.072725
(1 row)

Ale to wykorzystuje czas lokalny. Próba wymuszenia tego na UTC powoduje błąd składniowy:

postgres=# create temporary table test(id int, ts timestamp without time zone default now() at time zone 'utc');
ERROR:  syntax error at or near "at"
LINE 1: ...int, ts timestamp without time zone default now() at time zo...
Wichert Akkerman
źródło

Odpowiedzi:

297

Funkcja nie jest nawet potrzebna. Po prostu umieść nawiasy wokół domyślnego wyrażenia:

create temporary table test(
    id int, 
    ts timestamp without time zone default (now() at time zone 'utc')
);
Daniel Vérité
źródło
2
Widzę, jak funkcja now_utc () naprawdę świeci podczas pisania zapytań
misaxi
Działa to nawet wtedy, gdy czas cofa się o godzinę - funkcja now () zwraca znacznik czasu, który zna przesunięcie względem czasu UTC.
Clay Lenhart
1
FWIW, prowadzenie tej kwerendy w PostgreSQL 11.5: ALTER TABLE testcase_result ADD COLUMN date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW() AT TIME ZONE "UTC") NOT NULL;nie powiedzie się z: ERROR: column "UTC" does not exist. Upewnij się, że 'utc'wszystko jest pisane małymi literami.
code_dredd
2
Korekta: 'utc'ciąg musi być również umieszczony w apostrofach, a nie w podwójnych cudzysłowach.
code_dredd
56

Jeszcze inne rozwiązanie:

timezone('utc', now())
martti
źródło
26

Owiń to w funkcję:

create function now_utc() returns timestamp as $$
  select now() at time zone 'utc';
$$ language sql;

create temporary table test(
  id int,
  ts timestamp without time zone default now_utc()
);
Denis de Bernardy
źródło
16

Co powiesz na

now()::timestamp

Jeśli Twoja inna sygnatura czasowa nie zawiera strefy czasowej, to przesyłanie zwróci pasujący typ „znacznik czasu bez strefy czasowej” dla bieżącego czasu.

Chciałbym jednak przeczytać, co inni myślą o tej opcji. Nadal nie ufam swojemu zrozumieniu tych rzeczy „z / bez” strefy czasowej.

EDYCJA: Dodanie tutaj komentarza Michaela Ekoki, ponieważ wyjaśnia ważną kwestię:

Caveat. Pytanie dotyczy generowania domyślnego znacznika czasu w UTC dla kolumny znacznika czasu, która nie przechowuje strefy czasowej (być może dlatego, że nie ma potrzeby przechowywania strefy czasowej, jeśli wiesz, że wszystkie twoje sygnatury czasowe są takie same). To, co robi twoje rozwiązanie, to wygenerowanie lokalnego znacznika czasu (który dla większości ludzi niekoniecznie będzie ustawiony na UTC) i zapisanie go jako naiwnego znacznika czasu (takiego, który nie określa swojej strefy czasowej).

Risadinha
źródło
To interesująca sztuczka, ale może prowadzić do zamieszania, jeśli nie jesteś świadomy takiego zachowania. Przyjęta odpowiedź jest krystalicznie jasna, jeśli chodzi o działanie i pożądany wynik. To samo z funkcją, ale trzymam się z daleka od funkcji w DB ...
Frank V,
w mojej opcji generuje lokalną
datę i godzinę
Caveat. Pytanie dotyczy generowania domyślnego znacznika czasu w UTC dla kolumny znacznika czasu, która nie przechowuje strefy czasowej (być może dlatego, że nie ma potrzeby przechowywania strefy czasowej, jeśli wiesz, że wszystkie twoje sygnatury czasowe są takie same). Twoje rozwiązanie polega na wygenerowaniu lokalnego znacznika czasu (który dla większości ludzi niekoniecznie będzie ustawiony na UTC) i zapisaniu go jako naiwnego znacznika czasu (takiego, który nie określa swojej strefy czasowej).
Michael Ekoka
@MichaelEkoka Dodałem Twój komentarz do odpowiedzi - proszę, edytuj go, jeśli chcesz. Wyjaśniasz to bardzo jasno. Dzięki!
Risadinha
Ostrzeżenie : znacznik czasu z polem strefy czasowej NIE przechowuje strefy czasowej, wbrew rozsądnemu założeniu. Zobacz poniższe łącze i wyszukaj na stronie UTC. Na wejściu znacznik czasowy ze strefą czasową konwertuje przychodzącą wartość na utc i zapisuje tę wartość BEZ INFORMACJI O STREFIE CZASOWEJ I PRZESUNIĘCIU . Na wyjściu przechowywana wartość utc jest konwertowana na czas lokalny przy użyciu strefy czasowej klienta, jeśli jest dostępna. W tym typie chodzi o konwersję wartości w czasie zapytania. Powinieneś to przetestować, zapisując dane z jednej strefy czasowej i wybierając inną. postgresql.org/docs/11/datatype-datetime.html
Tom
7

Oto 2 równoważne rozwiązania:

(w poniższym kodzie, należy podstawić 'UTC'do strefy i now()dla znacznika czasu )

  1. timestamp AT TIME ZONE zone - zgodny ze standardem SQL
  2. timezone(zone, timestamp) - prawdopodobnie bardziej czytelny

Strefa czasowa funkcji (strefa, znacznik czasu) jest odpowiednikiem zgodnej z SQL sygnatury czasowej konstrukcji w strefie czasowej ATIME ZONE.


Wyjaśnienie:

  • strefę można określić jako ciąg tekstowy (np. 'UTC') lub jako interwał (np. INTERVAL '-08:00') - tutaj znajduje się lista wszystkich dostępnych stref czasowych
  • timestamp może być dowolną wartością typu timestamp
  • now()zwraca wartość typu znacznik czasu (dokładnie to, czego potrzebujemy) z dołączoną domyślną strefą czasową bazy danych (np 2018-11-11T12:07:22.3+05:00.).
  • timezone('UTC', now())zamienia nasz aktualny czas (typu znacznik czasu ze strefą czasową ) na bezczasowy odpowiednik w formacie UTC.
    Np . SELECT timestamp with time zone '2020-03-16 15:00:00-05' AT TIME ZONE 'UTC'Wróci 2020-03-16T20:00:00Z.

Dokumenty: strefa czasowa ()

lakesare
źródło
1
Dziękuję za podsumowanie tych rzeczy. Użyłem wersji z małymi literami at timezone 'utc'i to nie działa z moją konfiguracją PostgreSQL. Problem z użyciem wielkich liter rozwiązany
Geradlus_RU
2

Funkcja już istnieje: strefa czasowa ('UTC' :: text, now ())

user10259440
źródło