Postgres时间与时区比较

Postgres time comparaison with time zone

今天遇到一个奇怪的postgres行为。让我解释一下:

这是我的 table 我会继续努力。

=># \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)

当我尝试输入大量条目时,似乎在 start_time 和 CURRENT_TIME 之间进行了比较,但没有转换时区。

为了您的信息,我也试过了:

select * from planning_time_slot where start_time > timezone('Europe/Paris', CURRENT_TIME);

它有完全相同的结果。

我还尝试将列类型更改为 time(0) with time zone。它产生完全相同的结果。

最后一点很重要。我真的需要设置我想要的时区,因为稍后我会根据其他内容动态更改它。所以不会每次都是'Europe/Paris'。

有人有线索或提示吗?

psql (PostgreSQL) 11.2 (Debian 11.2-1.pgdg90+1)

我认为你有更深层次的问题。

您有一天、开始时间和结束时间,但没有时区的概念。因此,这将根据观察者的时区而有所不同。

我认为你应该添加一个 tz 列来存储信息所在的时区。然后你可以这样获取开始时间:

WHERE (day + start_time) AT TIME ZONE tz > current_timestamp
例如,

(CURRENT_TIME AT TIME ZONE 'Europe/Paris') 就是 17:52:17.872082+02。但在内部它是 15:52:17.872082+00。 time 和 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 转换为 time 会去掉时区。

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 的概念时,这种比较才有意义。例如,如果您有一个移动 phone 游戏,其中事件“在 17:00”开始,这意味着 17:00 用户所在的位置。这被称为“浮动时区”。


  • 假设 day 是“星期几”,我建议将其存储为整数。比较和本地化更容易。
  • 考虑单个 timerange. Then you can use range operators.
  • 而不是单独的开始和结束时间