Основни неща за разбиране
timestamp without time zone AT TIME ZONE
претълкува timestamp
като се намира в тази часова зона с цел конвертирането й в UTC .
timestamp with time zone AT TIME ZONE
преобразува a timestamptz
в timestamp
в посочената часова зона.
PostgreSQL използва ISO-8601 часови зони, които уточняват, че източно от Гринуич е положително ... освен ако не използвате спецификатор на часова зона POSIX, в който случай той следва POSIX. Настъпва лудост.
Защо първият дава неочакван резултат
Времевите марки и часовите зони в SQL са ужасни. Това:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
интерпретира въведения с непознат литерал '2011-12-30 00:30:00'
като timestamp without time zone
, което Pg предполага, че е в местната часова зона, освен ако не е казано друго. Когато използвате AT TIME ZONE
, той е (съгласно спецификацията) преинтерпретиран като timestamp with time zone
във часовата зона EST5EDT
след това се съхранява като абсолютно време в UTC - така че се преобразува от EST5EDT
до UTC, т.е. изместването на часовата зона се изважда . x - (-5)
е x + 5
.
Това клеймо за време, коригирано към UTC съхранение, след това се коригира за вашия сървър TimeZone
настройка за показване, така че да се показва в местно време.
Ако вместо това искате да кажете „Имам тази дата за време в UTC и искате да видя какво е еквивалентното местно време в EST5EDT“, ако искате да сте независими от настройката TimeZone на сървъра, трябва да напишете нещо като:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Това гласи:„Дадено времеви отпечатък 2011-12-30 00:30:00, третирайте го като клеймо за време в UTC, когато преобразувате в timestamptz, след което преобразувайте този timestamptz в местно време в EST5EDT.
Ужасно, нали? Искам да дам фирмен разговор с който реши лудата семантика на AT TIME ZONE
- наистина трябва да е нещо като timestamp CONVERT FROM TIME ZONE '-5'
и timestamptz CONVERT TO TIME ZONE '+5'
. Също така timestamp with time zone
всъщност трябва да носи часовата си зона със себе си, а не да се съхранява в UTC и автоматично да се преобразува в местно време.
Защо вторият работи (стига TimeZone =UTC)
Вашата оригинална "работна" версия:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
ще бъде правилно само ако TimeZone е настроен на UTC, тъй като преобразуването от текст към timestamptz предполага TimeZone, когато не е посочено.
Защо третото работи
Два проблема се отменят взаимно.
Другата версия, която изглежда работи, е независима от TimeZone, но работи само защото два проблема се самоотменят. Първо, както е обяснено по-горе, timestamp without time zone AT TIME ZONE
претълкува клеймото за дата е в тази часова зона за преобразуване в UTC timestamptz; това ефективно изважда изместване на часовата зона.
Въпреки това, по причини, които не са ми познати, PostgreSQL използва времеви печати с обратен знак на това, което съм свикнал да виждам на повечето места. Вижте документацията:
Друг проблем, който трябва да имате предвид, е, че в имената на часовите зони POSIX се използват положителни отмествания за местоположения западно от Гринуич. Навсякъде другаде PostgreSQL следва конвенцията ISO-8601, според която положителните измествания на часовата зона са на изток от Гринуич.
Това означава, че EST5EDT
е същото като +5
, а не -5
. Ето защо работи:защото изваждате tz отместването, а не го добавяте, но изваждате отрицателно изместване!
Това, което ви трябва, за да го направите правилно, е:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';