#postgresql #datetime #timezone
Вопрос:
Сегодня я сталкиваюсь со странным поведением postgres. Позвольте мне объяснить:
Вот мой стол, над которым я буду работать.
=># d planning_time_slot
Table "public.planning_time_slot"
Column | Type | Collation | Nullable | Default
------------- --------------------------- ----------- ---------- ------------------------------------------------
id | integer | | not null | nextval('planning_time_slot_id_seq'::regclass)
planning_id | integer | | not null |
day | character varying(255) | | not null |
start_time | time(0) without time zone | | not null |
end_time | time(0) without time zone | | not null |
day_id | integer | | not null | 0
Indexes:
"planning_time_slot_pkey" PRIMARY KEY, btree (id)
"idx_a9e3f3493d865311" btree (planning_id)
Foreign-key constraints:
"fk_a9e3f3493d865311" FOREIGN KEY (planning_id) REFERENCES planning(id)
то, что я хочу сделать, это что-то вроде:
select * from planning_time_slot where start_time > (CURRENT_TIME AT TIME ZONE 'Europe/Paris');
Но похоже, что postgres сравнивает время до преобразования часового пояса.
Вот мои тесты:
=># select * from planning_time_slot where start_time > (CURRENT_TIME AT TIME ZONE 'Europe/Paris');
id | planning_id | day | start_time | end_time | day_id
----- ------------- ----- ------------ ---------- --------
157 | 6 | su | 16:00:00 | 16:30:00 | 0
(1 row)
=># select (CURRENT_TIME AT TIME ZONE 'Europe/Paris');
timezone
--------------------
16:35:48.591002 02
(1 row)
Когда я пытаюсь с большим количеством записей, кажется, что сравнение выполняется между временем начала и текущим временем без приведения часового пояса.
К вашему сведению, я тоже пытался :
select * from planning_time_slot where start_time > timezone('Europe/Paris', CURRENT_TIME);
Это дает точно такой же результат.
Я также попытался изменить тип столбца на время(0) с указанием часового пояса. Это дает точно такой же результат.
И последний важный момент. Мне действительно нужно установить нужный часовой пояс, потому что позже я буду динамически изменять его в зависимости от других материалов. Так что это не будет каждый раз «Европа/Париж».
У кого-нибудь есть подсказка или подсказка, пожалуйста ?
psql (PostgreSQL) 11.2 (Debian 11.2-1.pgdg90 1)
Комментарии:
1. Используйте
localtime
вместоcurrent_time
и избавьтесь от всего, что связано с часовыми поясами2. Что
show timezone
возвращается?3. Я не могу отказаться от всего часового пояса, так как позже я буду динамически изменять часовой пояс, а не позволять ему быть просто Eur/Париж.
4. показать возврат часового пояса:
TimeZone ---------- UCT (1 row)
Ответ №1:
(CURRENT_TIME AT TIME ZONE 'Europe/Paris')
есть, например, 17:52:17.872082 02
. Но внутренне это так 15:52:17.872082 00
. И время, и timetz (время с часовым поясом) хранятся в формате UTC, с той лишь разницей, что timetz хранится с часовым поясом. Изменение часового пояса не меняет того, какой момент времени он представляет.
Поэтому, когда вы сравниваете это со временем…
# select '17:00:00'::time < '17:52:17 02'::timetz;
?column?
----------
f
Это действительно так…
# select '17:00:00'::time < '15:52:17'::time;
?column?
----------
f
Приведение timetz ко времени приведет к отключению часового пояса.
test=# select (CURRENT_TIME AT TIME ZONE 'Europe/Paris')::time;
timezone
-----------------
17:55:57.099863
(1 row)
test=# select '17:00:00' < (CURRENT_TIME AT TIME ZONE 'Europe/Paris')::time;
?column?
----------
t
Обратите внимание, что такое сравнение имеет смысл только в том случае, если вы хотите сохранить представление о том, что что-то происходит в 17:00 по часам на стене. Например, если у вас была игра на мобильном телефоне, в которой событие начинается «в 17:00», что означает 17:00, где находится пользователь. Это называется «плавающим часовым поясом».
- Предполагая
day
, что это «день недели», я предлагаю сохранить его в виде целого числа. Это проще сравнивать и локализовать. - Вместо раздельного времени начала и окончания рассмотрите одно
timerange
. Затем вы можете использовать операторы диапазона.
Комментарии:
1. Большое вам спасибо за ваше подробное объяснение ! Это очень хорошо работает, когда применяется ко времени. Кроме того, ваше представление о временном диапазоне очень хорошо подходит для моей цели, потому что позже мне, вероятно, понадобятся операторы диапазона.
Ответ №2:
Я думаю, что у вас есть более глубокие проблемы.
У вас есть день, время начала и время окончания, но нет понятия о часовом поясе. Таким образом, это будет означать что-то другое в зависимости от часового пояса наблюдателя.
Я думаю, вам следует добавить tz
столбец, в котором хранится информация о часовом поясе. Тогда вы можете получить время начала примерно так:
WHERE (day start_time) AT TIME ZONE tz > current_timestamp
Комментарии:
1. Спасибо вам за ваш ответ ! Действительно, у меня есть столбец часового пояса в другой таблице (планирование таблицы). Я также изучу ваш ответ с учетом этого. Это выглядит как отличная идея