Нека обясня двата примера:
И в двете приемаме часова зона UTC (т.е. SET timezone TO UTC
).
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
timezone
---------------------
2015-12-31 16:00:00
(1 row)
Това е еквивалентно на SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz)
, т.е. Postgres имплицитно преобразува низа в timestamptz
.
Знаем, че timezone
функция преобразува напред и назад между timestamp
и timestamptz
:
Тъй като му даваме timestamptz
като вход ще изведе timestamp
. С други думи, той преобразува абсолютната точка във времето 2016-01-01 00:00Z
до време на стена в US/Pacific
, т.е. какво е показвал часовникът в Лос Анджелис в този абсолютен момент от времето.
В пример 2 правим обратното, а именно вземаме timestamp
и преобразуването му в timestamptz
. С други думи, ние питаме:коя беше абсолютната точка във времето, когато часовникът в Лос Анджелис показваше 2016-01-01 00:00
?
Вие споменавате:
'2016-01-01 00:00'::timestamp
е timestamp
, т.е. време на стената. Той няма представа за часова зона.
Мисля, че може да не сте разбрали напълно разликата между timestamp
и timestamptz
, което е ключово тук. Мислете за тях като за време на стената , т.е. времето, показвано някъде по света на часовника, окачен на стената, и абсолютното време , т.е. абсолютното време в нашата вселена.
Примерите, които давате в собствения си отговор, не са съвсем точни.
SELECT ts FROM (VALUES
(timestamptz '2012-03-05 17:00:00+0') -- outputs 2012-03-05 17:00:00+00 --1
,(timestamptz '2012-03-05 18:00:00+1') -- outputs 2012-03-05 17:00:00+00 --2
,(timestamp '2012-03-05 18:00:00+1') -- outputs 2012-03-05 18:00:00+00 --3
,(timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6') -- outputs 2012-03-05 17:00:00+00 --4
,(timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC') -- outputs 2012-03-05 17:00:00+00 --5
,(timestamp '2012-03-05 17:00:00'::timestamp) -- outputs 2012-03-05 17:00:00+00 --6
,(timestamp '2012-03-05 17:00:00'::timestamptz) -- outputs 2012-03-05 17:00:00+00 --7
) t(ts);
Проблемът с вашия пример е, че конструирате един набор от данни с една колона. Тъй като една колона може да има само един тип, всеки ред (или отделна стойност в този случай) се преобразува в един и същи тип, а именно timestamptz
, въпреки че някои стойности са изчислени като timestamp
(напр. стойност 3). По този начин тук имате допълнително имплицитно преобразуване.
Нека разделим примера на отделни заявки и да видим какво се случва:
Пример 1
db=# SELECT timestamptz '2012-03-05 17:00:00+0';
timestamptz
------------------------
2012-03-05 17:00:00+00
Както може би вече знаете, timestamptz '2012-03-05 17:00:00+0'
и '2012-03-05 17:00:00+0'::timestamptz
са еквивалентни (предпочитам второто). Затова, за да използвам същия синтаксис като в статията, ще пренапиша:
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
Сега, какво става тук? Е, по-малко, отколкото в първоначалното ви обяснение. Низът просто се анализира като timestamptz
. Когато резултатът се отпечата, той използва текущо зададената timezone
config, за да го конвертирате обратно в четимо от човека представяне на основната структура от данни, т.е. 2012-03-05 17:00:00+00
.
Нека променим timezone
config и вижте какво се случва:
db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 18:00:00+01
Единственото нещо, което се промени, е как timestamptz
се отпечатва на екран, а именно с помощта на Европа/Берлин часова зона.
Пример 2
db=# SELECT timestamptz '2012-03-05 18:00:00+1';
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Отново, просто анализирам датата.
Пример 3
db=# SELECT timestamp '2012-03-05 18:00:00+1';
timestamp
---------------------
2012-03-05 18:00:00
(1 row)
Това е същото като '2012-03-05 18:00:00+1'::timestamp
. Това, което се случва тук е, че отместването на часовата зона просто се игнорира, защото вие искате timestamp
.
Пример 4
db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Нека пренапишем, за да бъде по-просто:
db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Това пита:какво беше абсолютното време, когато часовникът на стената в часовата зона с отместване от +6 часа показваше 2012-03-05 11:00:00
?
Пример 5
db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Нека пренапишем:
db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Това пита:какво беше абсолютното време, когато часовникът на стената в часовата зона UTC показваше 2012-03-05 17:00:00
?
Пример 6
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
Тук предавате два пъти към timestamp
, което няма разлика. Нека опростим:
db=# SELECT '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
Мисля, че е ясно.
Пример 7
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Нека пренапишем:
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Първо анализирате низа като timestamp
и след това го конвертирате в timestamptz
използвайки текущо зададената timezone
. Ако променим timezone
, получаваме нещо друго, защото Postgres приема тази часова зона, когато преобразува timestamp
(или низ без информация за часовата зона) към timestamptz
:
db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+01
(1 row)
Това абсолютно време, изразено в UTC, е 2012-03-05 16:00:00+00
, като по този начин се различава от оригиналния пример.
Надявам се това да изясни нещата. Отново разбиране на разликата между timestamp
и timestamptz
е ключово. Помислете за времето на стената спрямо абсолютното време.