PostgreSQL date () с часовым поясом

у меня проблема с правильным выбором дат из Postgres - они хранятся в UTC, но не преобразование с функцией Date() должным образом.

преобразование метки времени в дату дает мне неправильную дату, если она прошла 4pm PST.

2012-06-21 должно быть 2012-06-20 в этом случае.

на starts_at тип данных столбца timestamp without time zone. Вот мои вопросы:

без преобразования в часовой пояс PST:

Select starts_at from schedules where id = 40;

      starts_at      
---------------------
 2012-06-21 01:00:00

преобразование дает это:

Select (starts_at at time zone 'pst') from schedules where id = 40;
        timezone        
------------------------
 2012-06-21 02:00:00-07

но ни один из них не преобразуется в правильную дату в часовом поясе.

3 ответов


я не вижу точно типа starts_at в вашем вопросе. Вы действительно должны включить эту информацию,это ключ к решению. Я должен угадать.

В Основном, PostgreSQL всегда сохраняет значение времени UTC для типа timestamp with time zone внутренне. Только дисплей зависит от вашего текущего timezone настройка. Эффект AT TIME ZONE construct также изменяется с базовым типом данных. Больше подробности:

если вы извлекаете date тип timestamp [without time zone], вы получите дату для текущего часового пояса. День на выходе будет таким же, как и на дисплее timestamp значение.

если вы извлекаете date тип timestamp with time zone (timestamptz для краткости), смещение часового пояса сначала "применяется". У тебя все еще есть дата. для текущего часового пояса, который согласуется с дисплей метки времени. Тот же самый момент времени переводится на следующий день в некоторых частях Европы, когда в Калифорнии, например, уже перевалило за четыре часа дня. Чтобы получить дату для определенного часового пояса, примените AT TIME ZONE первый.

поэтому то, что вы описываете в верхней части вопроса, противоречит вашему примеру.

учитывая, что starts_at это timestamp [without time zone] и время на вашем сервере установлено на локальное время. Тест с:

SELECT now();

он отображает то же время, что и часы на стене? Если да (и сервер БД работает с правильным временем), то timezone настройка текущего сеанса согласуется с вашим местным часовым поясом. Если нет, вы можете посетить параметр timezone в своем postgresql.conf или ваш клиент для сессии. подробности в руководстве.

имейте в виду, что timezone смещение используется напротив знак то, что отображается в метки литералы. См.:

чтобы получить локальную дату от starts_at просто

SELECT starts_at::date

равносильно:

SELECT date(starts_at)

кстати, ваше местное время находится в UTC-7 прямо сейчас, а не UTC-8, потому что летнее время действует (не среди более ярких идей человеческой расы).

Тихоокеанское стандартное время (PST) обычно составляет 8 часов "раньше" (больше timestamp значение), чем UTC (универсальный часовой пояс), но в летнее время (как сейчас) это может быть 7 часов. Вот почему timestamptz отображается как 2012-06-21 02:00:00-07 в вашем примере. Конструкция AT TIME ZONE 'PST' учитывает летнее время. Эти два выражения дают разные результаты (один зимой, один летом) и могут привести к разным датам при приведении:

SELECT '2012-06-21 01:00:00'::timestamp AT TIME ZONE 'PST'
     , '2012-12-21 01:00:00'::timestamp AT TIME ZONE 'PST'

в основном то, что вы хотите:

$ select starts_at AT TIME ZONE 'UTC' AT TIME ZONE 'US/Pacific' from schedules where id = 40

я получил решение из этой статьи ниже, что является прямым золотом!!! Это объясняет эту нетривиальную проблему очень четко, дайте ей прочитать, если вы хотите лучше понять управление pstgrsql TZ.

выражение меток времени PostgreSQL без зон в локальном времени

вот что происходит. Во-первых, вы должны знать ,что " часовой пояс PST на 8 часов отстает от часового пояса UTC, так, например, 1 января 2014, 4:30 PM PST (Wed, 01 Jan 2014 16:00:30 -0800) эквивалентно 2 января 2014, 00: 30 AM UTC (Thu, 02 Jan 2014 00:00:30 +0000). Любое время после 4: 00 вечера в PST переходит на следующий день, интерпретируемый как UTC.

кроме того, как упоминалось выше Erwin Brandstetter, postresql имеет два типа типа данных временных меток: один с часовым поясом и один без. Если ваши метки времени включают часовой пояс, то простой:

$ select starts_at AT TIME ZONE 'US/Pacific' from schedules where id = 40

будет работать. Однако, если ваше время timezoneless, выполнение вышеуказанной команды не будет работать, и вы должны сначала преобразовать свою временную метку в временную метку с часовым поясом, а именно часовой пояс UTC, и только затем преобразовать ее в желаемый " PST " или "US/Pacific" (которые одинаковы до некоторых проблем с переходом на летнее время. Я думаю, вы должны быть в порядке с любым).

позвольте мне продемонстрировать пример, где я создаю timezoneless метки. Предположим для удобства, что наш местный часовой пояс действительно " PST " (если бы это было не так это становится немного сложнее,что не нужно для целей этого объяснения).

скажите, что у меня есть:

$ select timestamp '2014-01-2 00:30:00' AS a, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AS b,  timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE 'PST' AS c, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d

Это даст:

"a"=>"2014-01-02 00:30:00"   (This is the timezoneless timestamp)
"b"=>"2014-01-02 00:30:00+00" (This is the UTC TZ timestamp, note that up to a timezone, it is equivalent to the timezoneless one)
"c"=>"2014-01-01 16:30:00" (This is the correct 'PST' TZ conversion of the UTC timezone, if you read the documentation postgresql will not print the actual TZ for this conversion)
"d"=>"2014-01-02 08:30:00+00"

последняя метка времени является причиной всей путаницы в отношении преобразования timezoneless timestamp из UTC в " PST " в postgresql. Когда мы пишем:

timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d

мы берем timezoneless timestamp и пытаемся преобразовать его в ' PST TZ (мы косвенно предполагаем, что postgresql поймет что мы хотим, чтобы он конвертировал метку времени из UTC TZ, но у postresql есть свои собственные планы!). На практике, что в PostgreSQL делаем timezoneless типа timestamp ('2014-01-2 00:30:00) и относится к нему, как будто это уже было 'ПСТ' ТЗ метки (я.е: 2014-01-2 00:30:00 -0800) и преобразует, что для мирового часового пояса!!! Таким образом, он фактически толкает его на 8 часов вперед, а не назад! Таким образом, мы получаем (2014-01-02 08:30:00+00).

во всяком случае, это последнее (неинтуитивное) поведение является причиной всей путаницы. Прочитайте статью, если вы хотите более подробное объяснение, я на самом деле получил результаты, которые немного отличаются от их в этой последней части, но общая идея та же.


Я знаю, что это старый, но вы можете рассмотреть возможность использования в часовом поясе "US/Pacific" при кастинге, чтобы избежать проблем с PST/PDT. Так что

выберите starts_at:: TIMESTAMPTZ в часовом поясе "США / Тихий океан" из расписания, где ID = '40';