简体   繁体   English

Postgresql:如何从时间戳,时区字段正确创建带时区的时间戳

[英]Postgresql: how to correctly create timestamp with timezone from timestamp, timezone fields

I have a table with a timestamp without time zone. 我有一个没有时区的时间戳表。 YYYY-MM-DD HH:MM:SS YYYY-MM-DD HH:MM:SS

and a field "timezone" that is either "P" for Pacific or "M" for Mountain. 和一个字段“时区”,太平洋为“P”,山为“M”。

I need to create a field of type "timestamp with time zone" 我需要创建一个“带时区的时间戳”字段

Given the two fields I have, is there a way to do this that correctly accounts for Daylight Saving Time? 鉴于我有两个字段,有没有办法正确地解释夏令时?

Specifically: timestamp: 2013-11-03 01:00:00 timezone: "P" would become: 2013-11-03 01:00:00-07 具体来说:时间戳:2013-11-03 01:00:00时区:“P”将成为:2013-11-03 01:00:00-07

and timestamp: 2013-11-03 03:00:00 timezone: "P" would become: 2013-11-03 03:00:00-08 和时间戳:2013-11-03 03:00:00 timezone:“P”将成为:2013-11-03 03:00:00-08

First, when saying that the result would become for example 2013-11-03 01:00:00-07 , it should be added that this actually depends on the time zone setting of the SQL client. 首先,当说结果将成为例如2013-11-03 01:00:00-07 ,应该补充说这实际上取决于SQL客户端的时区设置。 For instance a session in european time will never read 2013-11-03 01:00:00-07 as the value of a timestamp with time zone because no european country is ever at GMT-07 . 例如,在欧洲时段会话永远不会阅读2013-11-03 01:00:00-07作为价值timestamp with time zone ,因为没有一个欧洲国家是有史以来在GMT-07

That said, the conversion can be done with the AT TIME ZONE construct applied to a timestamp without time zone . 也就是说,可以使用应用于timestamp without time zonetimestamp without time zoneAT TIME ZONE构造来完成转换。

Assuming we run this from the US/Pacific time zone: 假设我们从US/Pacific时区运行:

SET time zone 'US/Pacific';

SELECT t AT TIME ZONE 
     case z when 'P' then 'US/Pacific' when 'M' then 'US/Mountain' end  
  from (values
    ('2013-11-03 01:00:00'::timestamp, 'P'),
    ('2013-11-03 03:00:00'::timestamp, 'P')
  ) as v(t,z);

The result is: 结果是:

timezone        
------------------------
 2013-11-03 01:00:00-08
 2013-11-03 03:00:00-08

2013-11-03 01:00:00 AT time zone 'US/Pacific' has an ambiguity because it belongs to the hour span which happens first in the -07 timezone, and then a second time in the -08 timezone after the DST switch. 2013-11-03 01:00:00 AT time zone 'US/Pacific'有一个模糊性,因为它属于-07时区首先发生的小时跨度,然后是夏令时后-08时区的第二次开关。 The interpretation of postgres is to see it in the -08 timezone. postgres的解释是在-08时区看到它。 If we consider the minute before, it falls into the -07 timezone. 如果我们考虑前一分钟,它会落入-07时区。

The difference between TIMESTAMP WITHOUT TIME ZONE and TIMESTAMP WITH TIME ZONE ( TIMESTAMPTZ ) can be quite tricky to understand if you consider their names. 如果考虑他们的名字, TIMESTAMP WITHOUT TIME ZONETIMESTAMP WITH TIME ZONETIMESTAMPTZ )之间的区别可能非常棘手。 (In fact, the specifications seem to be sufficiently confusing so that various RDBMS implement it in a different way.) (事实上​​,规范似乎足够混乱,因此各种RDBMS以不同的方式实现它。)

In PostgreSQL, neither types store the time zone of the value when it was stored, but TIMESTAMPTZ stores the value as a precise instant in time based on the UTC reference, whereas TIMESTAMP WITHOUT TIME ZONE is always relative. 在PostgreSQL中,两种类型都不存储值存储时的时区,但TIMESTAMPTZ根据UTC引用将值存储为精确的即时时间,而TIMESTAMP WITHOUT TIME ZONE始终是相对的。

  • When queried, a TIMESTAMPTZ will be adjusted to represent the same instant in time as initially stored (in whichever part of the world this was) as the instant it would be in the current time zone as configured by the client. 查询时,将调整TIMESTAMPTZ以表示与最初存储的时间相同的时刻(在世界的任何一个部分),作为客户端配置的当前时区中的瞬间。
  • A TIMESTAMP WITHOUT TIME ZONE will always be the same value relative to the time zone configured by the client, even if the time zone where you query it from differs: the instant represented by 2013-11-03 03:00:00 will be ambiguous and depend on the client settings. TIMESTAMP WITHOUT TIME ZONETIMESTAMP WITHOUT TIME ZONE将始终与客户端配置的时区相同,即使您查询它的时区不同: 2013-11-03 03:00:00表示的2013-11-03 03:00:00将不明确并取决于客户端设置。

Presumably, you used your "timezone" column ( P or M ) with your TIMESTAMP WITHOUT TIME ZONE to compensate for the ambiguity in the input value. 据推测,您使用“时区”列( PM )和TIMESTAMP WITHOUT TIME ZONE来补偿输入值的模糊性。

In principle, if you are in the same relative time zone as the one where you stored the timestamp, you should get the same value back, so if you've set your client in the US/Pacific time zone and if you've stored 2013-11-03 03:00:00 in your P time zone, you should get 2013-11-03 03:00:00 back. 原则上,如果您与存储时间戳的时区位于相同的相对时区,则应该返回相同的值,因此如果您已将客户端设置为US/Pacific时区,并且您已存储2013-11-03 03:00:00在你的P时区,你应该得到2013-11-03 03:00:00 However, this is only valid when there is no ambiguity in the relative value. 但是,这仅在相对值没有歧义时才有效。

The problem here in your first example is that there already is some ambiguity: 你的第一个例子中的问题是已经存在一些歧义:

timestamp: 2013-11-03 01:00:00 timezone: "P" would become: 2013-11-03 01:00:00-07 时间戳:2013-11-03 01:00:00时区:“P”将成为:2013-11-03 01:00:00-07

2013-11-03 01:00:00 can represent two distinct instants in time in the US/Pacific time zone, so with just 2013-11-03 01:00:00 and "P" , you've already lost information that you won't be able to recover. 2013-11-03 01:00:00可以代表US/Pacific时区的两个不同时刻,所以只有2013-11-03 01:00:00"P" ,你已经丢失了信息你将无法恢复。

If you just wanted it to change between '-08' and '-07' depending on the DST setting at that instant in time, this would have been done for you automatically, but you should have use a TIMESTAMPTZ in the first place, to be precise on which instant in time you were representing. 如果您只是希望它在'-08'和'-07'之间切换,具体取决于该时刻的DST设置,这将自动为您完成,但您应该首先使用TIMESTAMPTZ ,确切地说,你代表哪个时刻。

Here is an example where the initial time zone is kept, so you can see the change between '-08' and '-07': 这是一个保留初始时区的示例,因此您可以看到'-08'和'-07'之间的变化:

SET time zone 'US/Pacific';

SELECT t AS "Date/Time for US/Pacific",
       t AT time zone 'UTC' "Date/Time in UTC"
FROM (VALUES
    ('2013-11-03 00:00:00-07'::timestamptz),
    ('2013-11-03 01:00:00-07'::timestamptz),
    ('2013-11-03 02:00:00-07'::timestamptz),
    ('2013-11-03 03:00:00-07'::timestamptz)) AS v(t);

Results: 结果:

| DATE/TIME FOR US/PACIFIC | DATE/TIME IN UTC    |
|--------------------------|---------------------|
| 2013-11-03 00:00:00-07   | 2013-11-03 07:00:00 |
| 2013-11-03 01:00:00-07   | 2013-11-03 08:00:00 |
| 2013-11-03 01:00:00-08   | 2013-11-03 09:00:00 |
| 2013-11-03 02:00:00-08   | 2013-11-03 10:00:00 |

Unfortunately, there is no way to handle DST change with just your two fields. 不幸的是,只有你的两个字段无法处理DST更改。

It's certainly worth reading the Date/Time types section of the PostgreSQL manual , as well as paying attention to the "return types" column of the table in the AT TIME ZONE documentation for a better understanding of these problems. 当然值得阅读PostgreSQL手册日期/时间类型部分 ,并注意AT TIME ZONE文档中表格的“返回类型”列,以便更好地理解这些问题。

Check if this makes any sense to you 检查这是否对您有意义

set timezone to 'PST8PDT';

select now();
              now              
-------------------------------
 2013-09-28 03:24:20.169189-07

select ts,
    ts at time zone 'PST' as "PST",
    ts at time zone 'PDT' as "PDT"
from (values
    ('2013-11-03 01:00:00'::timestamp),
    ('2013-11-03 02:00:00'),
    ('2013-11-03 03:00:00')
) s (ts)
;
         ts          |          PST           |          PDT           
---------------------+------------------------+------------------------
 2013-11-03 01:00:00 | 2013-11-03 01:00:00-08 | 2013-11-03 01:00:00-07
 2013-11-03 02:00:00 | 2013-11-03 02:00:00-08 | 2013-11-03 01:00:00-08
 2013-11-03 03:00:00 | 2013-11-03 03:00:00-08 | 2013-11-03 02:00:00-08

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM