从"没有时区的时间"和时区名称获取"时区时间"

Dam*_*son 25 sql postgresql time timezone

首先,我意识到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;
Run Code Online (Sandbox Code Playgroud)

结果(目前):

'10:00:00-08'
Run Code Online (Sandbox Code Playgroud)

但我真的想要:

'12:00:00-08'
Run Code Online (Sandbox Code Playgroud)

我找不到任何有关将时区应用到时间的文档,除此之外AT TIME ZONE.有没有办法在没有角色操纵或其他黑客的情况下实现这一目标?

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

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

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

更新2: 我为这种困惑道歉.该SCHEDULES表不使用time with time zone,我想选择一个time with time zone通过合并从价值观time without time zonetext时区的名字.

更新3: 感谢所有参与他们(热烈)讨论的人.:)我已经被说服放弃了我的计划,使用了time with time zone我的输出,而是使用了timestamp with time zone它,因为它表现良好,更具可读性,并解决了我将要遇到的另一个问题,即进入新日期的时区.IE浏览器."美国/温哥华"中的"2011-11-21 23:59"是"美国/里贾纳"中的"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;
Run Code Online (Sandbox Code Playgroud)

timezone()在我进入(current_date + SCHEDULES.time) AT TIME ZONE USERS.tz我的视图后,Postgres重写了这种格式.

Jon*_*eet 12

警告: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'
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

当然,当前系统日期可能与当地时区的当前日期不同.我认为这将解决这个问题......

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
Run Code Online (Sandbox Code Playgroud)

换一种说法:

  • 拿当前的瞬间
  • 计算用户时区的本地日期/时间
  • 截止日期
  • 将计划时间添加到该日期以获得 TIMESTAMP WITHOUT TIME ZONE
  • 使用AT TIME ZONE时间带适用于本地的日期/时间

我确信有更好的方法,但我认为这是有道理的.

您应该知道,在某些情况下,这可能会失败:

  • 在时钟从01:00跳到02:00的那一天,你想要的结果是01:30的时间,所以01:30根本不会发生?
  • 在时钟从02:00到01:00返回的那一天,你想要的结果是01:30,所以01:30会发生两次?