在Postgres的占DST,选择安排项目时(Accounting for DST in Postg

2019-07-03 13:59发布

我有时钟闹钟一个Postgres表(不是真的,但这个是类似的,并且更容易解释)。 警报是由用户提供了1个小时的分辨率设置,并且用户可以从很多不同的时区。 该报警器每天重复。 我要去拿,都应该在一天中的某一个小时走下车报警,和我有夏令时的问题 。 如何以最好的方式做到这一点?

阿尔弗雷德和洛塔都住在斯德哥尔摩(+1一小时内,UTC,但+ 2H时,它的DST)。
沙龙住在新加坡(+8小时内从UTC没有DST)

在冬季,阿尔弗雷德设置了4点报警。 该报警器应在当地时间凌晨4点熄灭,所有的一年。
在夏季,洛塔设置了5点报警。 再次,应该在上午5点一年四季都熄灭。
与此同时,沙龙已经为11:00报警。

所有这些都可以被存储在数据库中的UTC 03:00。

如果我查询报警冬季数据库应该在03:00 UTC熄灭,我想阿尔弗雷德和沙龙的警报。 新加坡目前是来自瑞典+ 7H,所以上午11点在新加坡是瑞典4点。 洛塔的报警不应该一小时熄灭。

相反,如果我查询报警夏天数据库应该在03:00 UTC熄灭,我想洛塔的和沙龙的警报。 新加坡从瑞典NOW + 6H,所以上午11点在新加坡上午5点在瑞典现在。 斯文的报警一个小时前就响了。

如何保存这一点,并查询数据库?

如果需要的话我可以改变DB模式。 目前,我们不调整夏时制,而事实上只是有“小时”整型字段(这似乎哑,一时间现场效果会更好)。

看来我需要存储既UTC时间和时区的信息,但我不知道如何以最佳方式Postgres里实现这一目标。 我发现Postgres有某种时区的概念,但没有时区现场,据我可以告诉类型。 另外,我想我需要做一些计算在SQL来确定如何抵消UTC时间在选择的基础上,时区数据和创建日期。 我不是伟大的SQL ...

我想解决这个Postgres里,因为可能有很多“警报”,我想避免附带取出所有的人都为Ruby和筛选有性能问题。 (是的,这是一个Rails应用程序。)

Answer 1:

使用timestamp with time zonetimestamptz进行计算)。
对于报警时间可time [without time zone]
但是你必须明确地保存的时区的每一行。

切勿使用time with time zone这是一个逻辑上破类型,它的使用在PostgreSQL气馁。 手动:

类型time with time zone由SQL标准定义的,但该定义显示会导致有问题的有用的性质。 在大多数情况下,组合datetimetimestamp without timezone ,和timestamp with time zone应该提供的任何应用程序所需要的日期/时间功能的完整范围。

演示设置:

CREATE TABLE alarm(name text, t time, tz text);
INSERT INTO alarm VALUES
  ('Alfred', '04:00', 'Europe/Stockholm') -- Alfred sets an alarm for 4 AM.
, ('Lotta',  '05:00', 'Europe/Stockholm') -- Lotta sets an alarm for 5 AM. 
, ('Sharon', '11:00', 'Asia/Singapore');  -- Sharon has set an alarm for 11 AM.

它必须是时区名称 (不缩写),以考虑DST。 有关:

  • 当施加到时间戳具有相同属性的时区名称产生不同的结果

获取“今天”匹配报警:

SELECT *
FROM   alarm
WHERE  (('2012-07-01'::date + t) AT TIME ZONE tz AT TIME ZONE 'UTC')::time
       = '03:00'::time
  • ('2012-7-1'::date + t) ...组装timestamp [without time zone]也可能只是now()::date + t “今天”的。
  • AT WITH TIME ZONE tz在保存时区...的地方时间戳,导致timestamptz
  • AT WITH TIME ZONE 'UTC' ......根据获得UTC timestamp
  • ::time ...提取时间成分最简单的方法。

在这里,你可以看一下时区名称 :

SELECT *
FROM   pg_timezone_names
WHERE  name ~~* '%sing%'
LIMIT  10

SQL小提琴展示夏季/冬季。



Answer 2:

你会使用一个完整的时区的名称,如美国/纽约,而不是EDT / EST,并存储在该时区的时间不UTC做到这一点。 然后,您可以保持幸福无知的夏令时的偏移量的变化。

像下面这样的东西应该工作:

-- CREATE TABLE time_test (
--   user_to_alert CHARACTER VARYING (30),
--   alarm_hour TIME,
--   user_timezone CHARACTER VARYING (30)
-- );

SELECT user_to_alert, 
  CASE
    WHEN EXTRACT(HOUR FROM CURRENT_TIME AT TIME ZONE user_timezone) = EXTRACT(HOUR FROM alarm_hour) THEN TRUE
  ELSE FALSE
END AS raise_alarm
FROM time_test;

要么:

SELECT user_to_alert
FROM time_test
WHERE EXTRACT(HOUR FROM CURRENT_TIME AT TIME ZONE user_timezone) = EXTRACT(HOUR FROM alarm_hour);


Answer 3:

鉴于:

SET timezone = 'UTC';

CREATE TABLE tzdemo (
    username text not null,
    alarm_time_utc time not null,
    alarm_tz_abbrev text not null,
    alarm_tz text not null
);

INSERT INTO tzdemo (username, alarm_time_utc, alarm_tz_abbrev, alarm_tz) VALUES
('Alfred', TIME '04:00' AT TIME ZONE '+01:00', 'CET', 'Europe/Stockholm'),
('Lotta', TIME '05:00' AT TIME ZONE '+02:00', 'CEST', 'Europe/Stockholm'),
('Sharon', TIME '11:00' AT TIME ZONE '+08:00', 'SGT', 'Singapore');

尝试:

SELECT username 
FROM tzdemo 
WHERE alarm_time_utc AT TIME ZONE alarm_tz_abbrev = TIME '03:00' AT TIME ZONE alarm_tz;

结果:

 username 
----------
 Alfred
 Sharon
(2 rows)

原理:

  • 存放时区报警创建抵消包括是否是或不是DST当时
  • 还存储转换为UTC时钟时间
  • 查询时,使用的事实,全时区名称遵循当前UTC规则倍,制作时间是在该地区目前的时区。 相较于在创建该警报时的时间段是什么保存的时间戳。

这也可以让你应对在用户改变位置,因此改变时区的情况。

这种方法可以按照日期进行资格时间戳,当你想要做的预测查询,如“在什么地方时候会发出报警声的位置”进行扩展。

我不是在这个解决方案完全有信心,并建议仔细测试。



文章来源: Accounting for DST in Postgres, when selecting scheduled items