我有时钟闹钟一个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应用程序。)
使用timestamp with time zone
( timestamptz
进行计算)。
对于报警时间可time [without time zone]
。
但是你必须明确地保存的时区的每一行。
切勿使用time with time zone
这是一个逻辑上破类型,它的使用在PostgreSQL气馁。 手动:
类型time with time zone
由SQL标准定义的,但该定义显示会导致有问题的有用的性质。 在大多数情况下,组合date
, time
, timestamp 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小提琴展示夏季/冬季。
你会使用一个完整的时区的名称,如美国/纽约,而不是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);
鉴于:
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规则倍,制作时间是在该地区目前的时区。 相较于在创建该警报时的时间段是什么保存的时间戳。
这也可以让你应对在用户改变位置,因此改变时区的情况。
这种方法可以按照日期进行资格时间戳,当你想要做的预测查询,如“在什么地方时候会发出报警声的位置”进行扩展。
我不是在这个解决方案完全有信心,并建议仔细测试。