Тип данных timestamp
- это краткое имя для timestamp without time zone
.
Другой вариант timestamptz
короток для timestamp with time zone
.
timestamptz
является предпочтительным типом в семье даты / времени, буквально. Он typispreferred
установил pg_type
, что может быть актуально:
Внутренняя память и эпоха
Внутренне временные метки занимают 8 байт памяти на диске и в оперативной памяти. Это целочисленное значение, представляющее количество микросекунд из эпохи Postgres, 2000-01-01 00:00:00 UTC.
Postgres также обладает встроенными знаниями об общепринятых секундах подсчета времени в UNIX с эпохи UNIX, 1970-01-01 00:00:00 UTC, и использует их в функциях to_timestamp(double precision)
или EXTRACT(EPOCH FROM timestamptz)
.
Исходный код:
* Метки времени, а также поля интервалов h / m / s хранятся как
* значения int64 с единицами микросекунд. (Когда-то они были
* двойные значения с единицами секунд.)
И:
/ * Эквиваленты Юлианской даты Дня 0 в расчете Unix и Postgres * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
Разрешение микросекунды преобразуется максимум в 6 дробных цифр за секунды.
### timestamp
Значение, набранное as, сообщает Postgres, что часовой пояс явно не указан. Текущий часовой пояс принят. Postgres игнорирует любой модификатор часового пояса, добавленный по ошибке!timestamp
[without time zone]
Часы не сдвигаются для отображения. С такой же настройкой часового пояса все в порядке. Для другого часового пояса значение меняется, но значение и отображение остаются прежними.
### timestamptz
Обработка timestamp with time zone
немного отличается. Я цитирую руководство здесь :
Для timestamp with time zone
, внутренне сохраненное значение всегда в UTC (Всемирное координированное время ...)
Жирный акцент мой. Сам часовой пояс никогда не сохраняется . Это модификатор ввода, используемый для вычисления соответствующей временной метки UTC, которая сохраняется, или модификатор вывода, используемый для вычисления отображаемого местного времени, с добавленным смещением часового пояса. Если вы не добавляете смещение для timestamptz
входных данных, предполагается текущая настройка часового пояса сеанса. Все вычисления выполняются со значениями меток времени UTC. Если вам нужно (или, возможно, придется) иметь дело с более чем одним часовым поясом, используйте timestamptz
.
Клиентам, таким как psql или pgAdmin или любому приложению, взаимодействующему через libpq (например, Ruby с гемом pg), предоставляется метка времени со смещением для текущего часового пояса или в соответствии с запрошенным часовым поясом (см. Ниже). Это всегда один и тот же момент времени , меняется только формат отображения. Или, как сказано в руководстве :
Все даты и время с учетом часового пояса хранятся внутри UTC. Они преобразуются в местное время в зоне, указанной
параметром конфигурации TimeZone, перед тем, как отобразиться клиенту.
Рассмотрим этот простой пример (в psql):
db = # ВЫБРАТЬ timestamptz '2012-03-05 20:00 +03 ';
timestamptz
------------------------
2012-03-05 18:00:00 +01
Жирный акцент мой. Что здесь случилось?
Я выбрал произвольное смещение часового пояса +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') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Возвращает 8 (или 9) одинаковых строк со столбцами timestamptz, содержащих одну и ту же метку времени UTC 2012-03-05 17:00:00
. Девятый ряд вроде работает в моем часовом поясе, но это злая ловушка. Увидеть ниже.
① Строки 6 - 8 с названием часового пояса и сокращением часового пояса для времени на Гавайях зависят от летнего времени (летнее время) и могут отличаться, хотя и не в настоящее время. Подобное имя часового пояса 'US/Hawaii'
знает о правилах перехода на летнее время и всех исторических сдвигах автоматически, в то время как сокращение как HST
просто тупой код для фиксированного смещения. Возможно, вам придется добавить другую аббревиатуру для летнего / стандартного времени. Имя правильно интерпретирует любую метку в данном часовом поясе. Аббревиатурой дешево, но он должен быть правильным для данной временной метки:
Летнее время не входит в число самых ярких идей, когда-либо выдвинутых человечеством.
9 Строка 9, помеченная как заряженное ружье, работает на меня , но только по стечению обстоятельств. Если вы явно приводите литерал к timestamp [without time zone]
, любое смещение часового пояса игнорируется ! Используется только голая метка времени. Затем значение автоматически приводится timestamptz
в примере для соответствия типу столбца. Для этого шага timezone
предполагается настройка текущего сеанса, который +1
в моем случае совпадает с часовым поясом (Европа / Вена). Но, вероятно, не в вашем случае - что приведет к другому значению. Короче говоря: не приводите timestamptz
литералы к timestamp
или вы потеряете смещение часового пояса.
##Ваши вопросы
Пользователь хранит время, скажем, 17 марта 2012 года, 19:00. Я не хочу, чтобы преобразования часового пояса или часовой пояс были сохранены.
Сам часовой пояс никогда не сохраняется. Используйте один из методов выше, чтобы ввести метку времени UTC.
Я использую указанный пользователем часовой пояс только для получения записей «до» или «после» текущего времени в местном часовом поясе пользователей.
Вы можете использовать один запрос для всех клиентов в разных часовых поясах.
Для абсолютного глобального времени:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Для времени по местным часам:
SELECT * FROM tbl WHERE time_col > now()::time
Еще не надоела справочная информация? Есть больше в руководстве.
Если вы хотите иметь дело в UTC по умолчанию:
В
config/application.rb
добавьте:Затем, если вы храните имя текущего часового пояса пользователя,
current_user.timezone
вы можете сказать.current_user.timezone
должно быть действительным именем часового пояса, в противном случае вы получитеArgumentError: Invalid Timezone
, см. полный список .источник