一尘不染

从“无时区的动画”和时区名称中获取“有时区的ime”。

sql

首先,我意识到time with time zone不推荐这样做。之所以要使用它,是因为我要比较多个time with time zone值与当前系统时间(不考虑日期)。也就是说,用户说每天的开始时间是08:00,结束时间是12:00,是他们的时区,而不是系统时区。因此,我time without time zone在一个表中有一个列,我们称之为它SCHEDULES.time,在另一个表中有一个 UNIX时区名称
列,我们称之为它USERS.tz

我的系统时区是'America/Regina',不使用DST,因此偏移始终是-06

给定时间“ 12:00:00”和“ tz / America / Vancouver”(美国/温哥华),我想将数据选择为类型的列,time with time zone但由于用户已经有效地将时间转换为我的时区,所以我不想说是从温哥华的12:00开始,而不是里贾纳。

因此,请执行以下操作:

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'

除之外,我找不到与将时区应用于时间有关的任何文档AT TIME ZONE。有没有一种方法可以在没有角色操纵或其他黑客攻击的情况下完成此任务?

更新: 这可以通过使用字符串连接,强制转换和Postgres时区视图来实现,例如:

select ('12:00:00'::text || utc_offset::text)::timetz
from pg_timezone_names
where name = 'America/Vancouver';

但是,这相当慢。必须有更好的方法,不是吗?

更新2: 对于造成的困惑,我深表歉意。该SCHEDULES表未使用time with time zone,我正在尝试time with time zone通过组合来自time without time zonetext时区名称的值来选择a 。

更新3: 感谢所有相关人员的(激烈的)讨论。:)我一直坚信放弃将atime with time zone用作输出结果的计划,而是改用a,timestamp with time zone因为它表现良好,更具可读性,并且解决了我将要遇到的另一个问题,即时区滚动到新日期。IE。“ America / Vancouver”中的“
2011-11-21 23:59”是“ America / Regina”中的“ 2011-11-22”。

更新4: 正如我在上一次更新中所说,我选择了@ MichaelKrelin-
hacker首次提出并最终确定@JonSkeet的答案。也就是说,timestamp with time zone作为我的最终输出是更好的解决方案。我最终使用了类似的查询:

SELECT timezone(USERS.tz, now()::date + SCHEDULES.time)
FROM SCHEDULES
JOIN USERS ON USERS.ID = SCHEDULES.USERID;

timezone()进入(current_date + SCHEDULES.time) AT TIME ZONE USERS.tz视图后,该格式由Postgres重写。


阅读 233

收藏
2021-03-17

共1个答案

一尘不染

警告:PostgreSQL新手(请参阅有关此问题的评论!)。我了解了一下时区了,所以我知道是什么使 问。

在我看来,这基本上是不受支持的情况(不幸的是)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时间带适用于本地的日期/时间

我敢肯定有更好的方法,但是我 认为 这是有道理的。

您应该意识到,在某些情况下,这可能会失败:

  • 您希望时钟在一天从01:00跳到02:00的那一天的01:30的结果是什么,因此根本不会发生01:30?
  • 您希望时钟从02:00返回至01:00的某一天的01:30的结果是什么,因此01:30发生两次?
2021-03-17