首先,这不是将 UTC转换为TZ 的重复问题。那不是我要的答案。我也不在寻找有关时间 偏移量 的答案。
有一堆按照用户指定的时间表运行的作业,应该尊重夏时制,夏令时,冬令时等。
基本上,我有一个这样的表:
name | timezone | usertime
------+---------------------+----------
Joe | America/New_York | 02:00:00
Jane | America/Chicago | 02:00:00
John | America/Denver | 02:00:00
Jess | America/Phoenix | 02:00:00
Jack | America/Los_Angeles | 02:00:00
Ping | Asia/Shanghai | 02:00:00
我想问的问题是:
如果我从世界标准时间
now()
开始算起,那么usertime
的UTC时间是(或将是)什么时间?
WITH TIME ZONE
=>错误! time
类型是……毫无价值。 postgres文档甚至对此警告,指责SQL标准,但其实现是为了完整性。
timestamp
更好,但是...
WITHOUT TIME ZONE
实际上确实应用了本地系统tz WITH TIME ZONE
需要一个时区,但只保存偏移量似乎最好只使用文本类型。
AT TIME ZONE
=>错误!此转换完全倒退。首先,无论时区设置如何,它都会显示与本地系统时间相关的所有内容。它还会向后(到本地时间)而不是向前(到设置的时区)计数。
SELECT
name,
timezone,
usertime,
usertime::time AT TIME ZONE timezone AS time
FROM demo;
name | timezone | usertime | time
------+---------------------+----------+-------------
Joe | America/New_York | 02:00:00 | 04:00:00-04
Jane | America/Chicago | 02:00:00 | 03:00:00-05
John | America/Denver | 02:00:00 | 02:00:00-06
Jess | America/Phoenix | 02:00:00 | 01:00:00-07
Jack | America/Los_Angeles | 02:00:00 | 01:00:00-07
Ping | Asia/Shanghai | 02:00:00 | 16:00:00+08
(5 rows)
正如您在此处看到的,time
类型实际上根本不允许时区,只是时间偏移量:
SELECT
name,
timezone,
(usertime || ' ' || timezone)::time AT TIME ZONE 'UTC'
AS realtime
FROM demo;
ERROR: invalid input syntax for type time: "02:00:00 America/New_York"
您会认为,如果您使用时区(例如1985-10-26 09:00:00 America/Los_Angeles
)显式构造了字符串,则将获得正确的时间。不。它完全忽略了它。
SELECT
name,
timezone,
(current_date || ' ' || usertime || ' ' || timezone)::timestamp,
usertime
FROM demo;
name | timezone | timestamp | usertime
------+---------------------+---------------------+----------
Joe | America/New_York | 2019-06-17 02:00:00 | 02:00:00
Jane | America/Chicago | 2019-06-17 02:00:00 | 02:00:00
John | America/Denver | 2019-06-17 02:00:00 | 02:00:00
Jess | America/Phoenix | 2019-06-17 02:00:00 | 02:00:00
Jack | America/Los_Angeles | 2019-06-17 02:00:00 | 02:00:00
Ping | Asia/Shanghai | 2019-06-17 02:00:00 | 02:00:00
(5 rows)
答案 0 :(得分:0)
这是低效率的全表扫描,但我认为它可以正常工作。
您必须
这是极其精致。最细微的修改(日期/时间戳,括号,顺序)可能会导致某些操作无效。
SELECT
name,
timezone,
usertime,
((current_timestamp AT TIME ZONE timezone)::date + usertime)
AT TIME ZONE timezone
AT TIME ZONE 'UTC'
AS realtime
FROM demo;
name | timezone | usertime | realtime
------+---------------------+----------+---------------------
Ping | Asia/Shanghai | 02:00:00 | 2019-06-17 18:00:00
Joe | America/New_York | 02:00:00 | 2019-06-18 06:00:00
Jane | America/Chicago | 02:00:00 | 2019-06-18 07:00:00
John | America/Denver | 02:00:00 | 2019-06-18 08:00:00
Jess | America/Phoenix | 02:00:00 | 2019-06-17 09:00:00
Jack | America/Los_Angeles | 02:00:00 | 2019-06-17 09:00:00
您必须使用current_timestamp
,而不是current_date
,并且必须将其强制转换为目标时区,并将其全部包装在括号中,否则您将有时间倒退或侧身:>
Jack | America/Los_Angeles | 02:00:00 | 2019-06-17 09:00:00
Ping | Asia/Shanghai | 02:00:00 | 2019-06-16 18:00:00
已更新:我不确定上一个示例中是否确实涵盖了100%的时区。不管您采用哪种方式,似乎都发生“向后滚动”,只是在不同区域的不同时间。
另一种方法是删除日期信息并滚动 current 时间以获取正确的事件查询窗口:
SELECT *
FROM (
SELECT
name,
timezone,
usertime,
(((current_timestamp AT TIME ZONE timezone)::date + usertime)
AT TIME ZONE timezone
AT TIME ZONE 'UTC')::time
AS utc_time,
(((current_timestamp AT TIME ZONE timezone)::date + usertime)
AT TIME ZONE timezone
AT TIME ZONE 'Indian/Chagos')::time
AS ctu_time
FROM demo
) as d
WHERE utc_time
BETWEEN (current_timestamp AT TIME ZONE 'UTC')::time
AND (current_timestamp AT TIME ZONE 'UTC')::time + INTERVAL '15 MINUTES'
OR ctu_time
BETWEEN (current_timestamp AT TIME ZONE 'Indian/Chagos')::time
AND (current_timestamp AT TIME ZONE 'Indian/Chagos')::time + INTERVAL '15 MINUTES'
;
这里的问题是23:58
和00:13
之间没有数字,因此您需要选择两个时区。
我希望看到一种解决方案,该解决方案始终显示事件的未来日期/时间,而不是过去的日期/时间。
我也想知道是否有一种方法可以查询WHERE realtime >= now() AND realtime < near_future
之类的内容而不会引起全表扫描。