Сравнение времени Postgres с часовым поясом

#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. Спасибо вам за ваш ответ ! Действительно, у меня есть столбец часового пояса в другой таблице (планирование таблицы). Я также изучу ваш ответ с учетом этого. Это выглядит как отличная идея