首先,我发现不建议使用time with time zone
。我将使用它,因为我将多个time with time zone
值与我当前的系统时间进行比较,而不管是哪一天。即用户说每天从08:00开始,在他们的时区12点结束,而不是系统时区。所以,我在一个表中有一个time without time zone
列,我们称之为SCHEDULES.time
,我在另一个表中有一个 UNIX时区名称列,我们称之为{{1} }。
我的系统时区为USERS.tz
,不使用DST,因此偏移量始终为'America/Regina'
。
给定时间为'12:00:00'并且tz为'America / Vancouver'我想将数据选择到-06
类型的列中,但我不想将时间转换为我的时区因为用户已经有效地说是从温哥华的12:00开始,而不是在里贾纳。
因此,做:
time with time zone
结果(目前):
SELECT SCHEDULES.time AT TIME ZONE USERS.tz
FROM SCHEDULES JOIN USERS on USERS.ID=SCHEDULES.USERID;
但我真的想要:
'10:00:00-08'
除了'12:00:00-08'
之外,我找不到任何与时区相关的文档。有没有办法在没有角色操纵或其他黑客的情况下实现这一目标?
更新 这可以通过使用字符串连接,强制转换和Postgres时区视图来实现:
AT TIME ZONE
然而,这是相当慢的。必须有更好的方法,不是吗?
更新2:
我为这种困惑道歉。 select ('12:00:00'::text || utc_offset::text)::timetz
from pg_timezone_names
where name = 'America/Vancouver';
表格不使用SCHEDULES
,我尝试通过合并time with time zone
和time with time zone
时区名称中的值来选择time without time zone
。
更新3:
感谢所有参与他们(热烈)讨论的人。 :)我一直坚信放弃我的计划使用text
作为我的输出,而是使用time with time zone
,因为它表现良好,更具可读性,并解决了我将遇到的另一个问题,滚动到新日期的时区。 IE浏览器。 “美国/温哥华”中的“2011-11-21 23:59”是“美国/里贾纳”中的“2011-11-22”。
更新4:
正如我在上次更新中所说,我选择了@ MichaelKrelin-hacker首先提出的答案和@JonSkeet最终确定的答案。也就是说,timestamp with time zone
作为我的最终输出是更好的解决方案。我最终使用了一个类似的查询:
timestamp with time zone
在我将SELECT timezone(USERS.tz, now()::date + SCHEDULES.time)
FROM SCHEDULES
JOIN USERS ON USERS.ID = SCHEDULES.USERID;
输入我的视图后,Postgres重写了timezone()
格式。
答案 0 :(得分:12)
警告:PostgreSQL新手(请参阅有关该问题的评论!)。我对时区有点了解,所以我知道是什么让 sense 问。
在我看来,AT TIME ZONE
这基本上是一种不受支持的情况(不幸的是)。查看AT TIME ZONE文档,它提供了一个表,其中“输入”值类型仅为:
我们错过了你想要的那个:时间没有时区。你要问的是有些逻辑,虽然它确实取决于日期......因为不同的时区可以根据日期有不同的偏移量。例如,12:00:00欧洲/伦敦可能意味着12:00:00 UTC,或者它可能意味着11:00:00 UTC,具体取决于它是冬天还是夏天。
在我的系统上,将系统时区设置为America / Regina,查询
SELECT ('2011-11-22T12:00:00'::TIMESTAMP WITHOUT TIME ZONE)
AT TIME ZONE 'America/Vancouver'
结果给了我2011-11-22 14:00:00-06
。这不是理想,但它确实至少给出了即时时间点(我认为)。我相信如果您使用客户端库获取它 - 或者将其与另一个TIMESTAMP WITH TIME ZONE
进行比较 - 您将获得正确的结果。只是文本转换然后使用系统时区进行输出。
这对你来说足够好吗?您是否可以将SCHEDULES.time
字段更改为TIMESTAMP WITHOUT TIME ZONE
字段,或者(在查询时)将字段中的时间与日期相结合以创建没有时区的时间戳?
编辑:如果您对“当前日期”感到满意,看起来就像您可以将查询更改为:
SELECT (current_date + SCHEDULES.time) AT TIME ZONE USERS.tz
from SCHEDULES JOIN USERS on USERS.ID=SCHEDULES.USERID
当然,当前系统日期可能与当地时区中的当前日期不同。我认为这将解决这个问题......
SELECT ((current_timestamp AT TIME ZONE USERS.tz)::DATE + schedules.time)
AT TIME ZONE USERS.tz
from SCHEDULES JOIN USERS on USERS.ID=SCHEDULES.USERID
换句话说:
TIMESTAMP WITHOUT TIME ZONE
AT TIME ZONE
将时区应用于本地日期/时间我确信有更好的方法,但我认为这是有道理的。
你应该知道,在某些情况下,这可能会失败:
答案 1 :(得分:3)
以下是如何计算未投射到文本的时间的演示:
CREATE TEMP TABLE schedule(t time, tz text);
INSERT INTO schedule values
('12:00:00', 'America/Vancouver')
,('12:00:00', 'US/Mountain')
,('12:00:00', 'America/Regina');
SELECT s.t AT TIME ZONE s.tz
- p.utc_offset
+ EXTRACT (timezone from now()) * interval '1s'
FROM schedule s
JOIN pg_timezone_names p ON s.tz = p.name;
基本上你必须减去UTC偏移并添加当地时区的偏移量以到达给定的时区。
您可以通过硬编码本地偏移来加快计算速度。在你的情况下(美国/里贾纳)应该是:
SELECT s.t AT TIME ZONE s.tz
- p.utc_offset
- interval '6h'
FROM schedule s
JOIN pg_timezone_names p ON s.tz = p.name;
由于pg_timezone_names
是一个视图,实际上并不是一个系统表,因此速度相当慢 - 就像演示的变体一样,可以转换为文本表示并返回。
我会存储时区缩写并通过text
进行双重演员,而无需加入pg_timezone_names
以获得最佳效果。
使你减速的罪魁祸首是pg_timezone_names
。经过一些测试后,我发现pg_timezone_abbrevs
远远优于PST
。当然,您必须保存正确的 时区缩写而不是时区名称才能实现此目的。时区名称自动考虑DST,时区缩写基本上只是时间偏移的代码。 The documentation:
时区缩写,例如
SELECT * FROM pg_timezone_names;
。这样的说明仅仅是 与全时区域名称相比,定义了与UTC的特定偏移量 这也意味着一套夏令时过渡日期规则。
查看这些测试结果或尝试自己:
SELECT * FROM pg_timezone_abbrevs;
总运行时间: 541.007 ms
text
总运行时间: 0.523 ms
因素1000 。是否按照您的想法转换为timetz
并返回pg_timezone_names
或使用我的方法来计算时间并不重要。两种方法都非常快。只是不要使用CREATE TEMP TABLE schedule(t time, abbrev text);
INSERT INTO schedule values
('12:00:00', 'PST') -- 'America/Vancouver'
,('12:00:00', 'MST') -- 'US/Mountain'
,('12:00:00', 'CST'); -- 'America/Regina'
-- calculating
SELECT s.t AT TIME ZONE s.abbrev
- a.utc_offset
+ EXTRACT (timezone from now()) * interval '1s'
FROM schedule s
JOIN pg_timezone_abbrevs a USING (abbrev);
-- casting (even faster!)
SELECT (t::text || abbrev)::timetz
FROM schedule s;
。
实际上,只要您保存时区缩写,就可以在没有任何其他连接的情况下采用投射路线。使用缩写而不是utc_offset。根据您的定义,结果是准确的。
{{1}}