Тип данных timestamp
- короткое имя для timestamp without time zone
.
Другой вариант timestamptz
- это сокращение от timestamp with time zone
.
timestamptz
- это буквально предпочтительный тип в семье даты / времени.typispreferred
установлено в pg_type
, что может иметь значение:
Эпоха
Внутренне , временные метки сохраняются как отсчет от эпохи .Postgres использует эпоху первого момента первого дня 2000 года в UTC, то есть 2000-01-01T00: 00: 00Z.Восемь октетов используются для хранения счетного числа.В зависимости от параметра времени компиляции это число может быть либо:
- 8-байтовое целое число (по умолчанию), с 0 до 6 цифрами дробной секунды
- Число с плавающей запятой (устарело), с 0 до 10 цифр доли секунды, где точность быстро снижается для значений, удаленных от эпохи.
В современных установках Postgres используется 8-байтовое целое число.
Обратите внимание, что Postgres не использует Unix time .Эпоха Postgres - это первый момент 2000-01-01, а не Unix 1970-01-01.В то время как время Unix имеет разрешение целых секунд, Postgres сохраняет доли секунд.
timestamp
Если вы определяете тип данных timestamp
[without time zone]
, вы говорите Postgres: «Я не предоставляю часовой пояс явно, предположим, чтовместо этого текущий часовой пояс. Postgres сохраняет временную метку как есть - , игнорируя модификатор часового пояса, если вы добавите его!
Когда вы позже отобразите эту timestamp
,Вы получаете обратно то, что ввели буквально. С той же настройкой часового пояса все в порядке. Если настройки часового пояса для сеанса изменяются, то значение timestamp
- значение остается прежним.
timestamptz
Обработка timestamp with time zone
немного отличается. Я цитирую руководство здесь :
Для timestamp with time zone
внутренне сохраненное значение равно всегда в UTC (Всемирное координированное время ...)
Мой жирный шрифт. Сам часовой пояс никогда не бываетсохранено . Это модификатор ввода, используемый для вычисления соответствующей метки времени UTC, которая сохраняется - или и модификатор выводаОн раньше вычислял местное время для отображения с добавленным смещением часового пояса.Если вы не добавляете смещение для timestamptz
на входе, предполагается текущая настройка часового пояса сеанса.Все вычисления выполняются со значениями меток времени UTC.Если вам нужно (или может потребоваться) иметь дело с несколькими часовыми поясами, используйте timestamptz
.
Клиенты, такие как psql или pgAdmin, или любое приложение, связывающееся через libpq (например, Ruby сpg gem) представлены с отметкой времени плюс смещение для текущего часового пояса или в соответствии с запрошенным часовым поясом (см. ниже).Это всегда один и тот же момент времени , меняется только формат отображения.Или , как указано в руководстве :
Все даты и время с учетом часового пояса хранятся внутри UTC.Они преобразуются в местное время в зоне, указанной параметром конфигурации TimeZone , перед тем как отобразиться на клиенте.
Рассмотрим этот простой пример (в psql):
db=# SELECT timestamptz '2012-03-05 20:00<b>+03</b>';
timestamptz
------------------------
2012-03-05 18:00:00<b>+01</b>
Смелый акцент мой. Что здесь произошло?
Я выбрал произвольное смещение часового пояса +3
для входного литерала.Для Postgres это только один из многих способов ввода метки времени UTC 2012-03-05 17:00:00
.Результат запроса отображается для текущего значения часового пояса Вена / Австрия в моем тесте, который имеет смещение +1
зимой и +2
летом: 2012-03-05 18:00:00+01
, потому что оно выпадает на зимнее время.
Postgres уже забыл, как это значение было введено.Все, что он помнит, это значение и тип данных.Так же, как с десятичным числом.numeric '003.4'
, numeric '3.40'
или numeric '+3.4'
- все приводят к одному и тому же внутреннему значению.
AT TIME ZONE
Как только вы овладеете этой логикой, вы сможете сделатьвсе, что вы хотите.Все, чего сейчас не хватает, - это инструмент для интерпретации или представления литералов меток времени в соответствии с конкретным часовым поясом.Вот тут и появляется конструкция AT TIME ZONE
. Есть два разных варианта использования.timestamptz
преобразуется в timestamp
и наоборот.
Для ввода UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
..., что эквивалентно:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Для отображения того же момента времени, что и EST timestamp
(восточное стандартное время):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Это верно, AT TIME ZONE 'UTC'
дважды .Первая интерпретирует значение timestamp
как (заданную) метку времени UTC, возвращающую тип timestamptz
.Второй преобразует timestamptz
в timestamp
в заданном часовом поясе 'EST' - что часы в часовом поясе EST отображают в этот уникальный момент времени.
Примеры
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
<strike>, (9, timestamp '2012-03-05 18:00:00+1')</strike> -- ② loaded footgun!
) t(id, ts);
Возвращает 8 (или 9) идентичных строк со столбцами timestamptz, содержащих одинаковую метку времени UTC 2012-03-05 17:00:00
.Девятый ряд вроде работает в моем часовом поясе, но это злая ловушка.См. Ниже.
① Строки 6 - 8 с часовым поясом имя и часовым поясом сокращение для времени на Гавайях:подвержены летнему времени (летнее время) и могут отличаться, но не в настоящее время.Название часового пояса, например 'US/Hawaii'
, знает о правилах перехода на летнее время и всех исторических сдвигах автоматически, а сокращение, например HST
, является просто тупым кодом для фиксированного смещения.Возможно, вам придется добавить другую аббревиатуру для летнего / стандартного времени. имя правильно интерпретирует любую метку времени в данном часовом поясе.Аббревиатура дешевая, но должна быть правильной для данной временной метки:
Переход на летнее время не входит в число самых ярких идей, когда-либо выдвинутых человечеством.
② Строка 9, отмеченная как заряженный ноготь у меня работает , но только по стечению обстоятельств.Если вы явно приводите литерал к timestamp [without time zone]
, , любое смещение часового пояса игнорируется !Используется только голая метка времени.Затем значение автоматически приводится к timestamptz
в примере, чтобы соответствовать типу столбца.Для этого шага предполагается настройка timezone
текущего сеанса, которая в моем случае совпадает с часовым поясом +1
(Европа / Вена).Но, вероятно, не в вашем случае - что приведет к другому значению.Вкратце: не приводите timestamptz
литералов к timestamp
, иначе вы потеряете смещение часового пояса.
Ваши вопросы
Пользователь сохраняет время, скажем, 17 марта 2012 г.7 вечера.Я не хочу, чтобы преобразования часового пояса или часовой пояс были сохранены.
Сам часовой пояс никогда не сохраняется.Используйте один из перечисленных выше способов для ввода метки времени UTC.
Я использую указанный пользователем часовой пояс только для получения записей «до» или «после» текущего времени в местном часовом поясе пользователей.
Вы можете использовать один запрос для всех клиентов в разных часовых поясах.
Для абсолютного глобального времени:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Для времени по местным часам:
SELECT * FROM tbl WHERE time_col > now()::time
Еще не надоела справочная информация? В руководстве есть больше.