为什么postgresql用时间戳欺骗我?

时间:2018-03-22 05:35:49

标签: postgresql timezone timestamp

我正在研究一个系统,我正在尝试最小化时区可能引入的错误,因此我在(postgresql)数据库上使用时间戳字段,但是当我创建记录时,我使用UNIX时代的秒数当我读取记录时(使用EXTRACT(EPOCH)阅读,插入时使用TO_TIMESTAMP())。

这样我就可以摆脱时区问题。或者我不过。在挖掘了一点之后我发现postgresql在从表中读取值时会有点困惑。请考虑以下问题:

select current_timestamp, extract(EPOCH from current_timestamp), id, last_gps_read, 
    extract(EPOCH from current_timestamp) - extract(EPOCH 
from last_gps_read) from sometable where id=1

哪个给出了

              now              |    date_part     | id |      last_gps_read       |    ?column?     
-------------------------------+------------------+----+--------------------------+-----------------
 2018-03-21 23:26:07.263931-06 | 1521696367.26393 |  1 | 2018-03-21 23:26:00.5273 | 21606.736631155

注意日期彼此之间的距离非常接近(只有大约7秒的差异?)。

所以,当我使用extract(EPOCH from x)技巧时,虽然差异会给我7秒钟......而是我得到~21607(我在GMT-6上,这就解释了为什么它是一些21600秒的差异)。这绝对不是很酷,因为这意味着当报告两个日期的UNIX纪元以来的秒数时,它会在报告来自表格的数据的秒数时以某种方式引入时区(我刚刚检查过,因为current_timestamp的UNIX纪元以来的秒数是正确的)。

这是什么理由?因为它听起来很像我的错误。

PS我可以考虑更改DB上的字段类型以使用整数来保存自UNIX纪元以来的实际秒数,所以我绝对摆脱了这一点,但这听起来有点矫枉过正。

2 个答案:

答案 0 :(得分:1)

不同之处在于EXTRACT(EPOCH FROM ...)始终计算相对于GMT时间(众所周知的01.01.1970 00:00:00+00)的时间。如果当前时间 转换为GMT,您将获得时间戳2018-03-21 17:26:00.5273+00,其差异为6小时至约7秒,或约{{1来自GPS时间戳。

您可以将GPS时​​间转换为当地时间,或者从时间戳差异中减去6 * 3600 + 7 = 21607以获得所需的结果。

答案 1 :(得分:0)

有些加倍@clemens回答样品......

匹配您的时区:

t=# set timezone to 'GMT+6';
SET

你做了什么:

t=# with c("ct", last_gps_read) as (values('2018-03-21 23:26:07.263931-06'::timestamptz,'2018-03-21 23:26:00.5273'::timestamp))
select ct
, extract(EPOCH from ct)
, last_gps_read
, extract(EPOCH from ct) - extract(EPOCH from last_gps_read)
from c;
              ct               |    date_part     |      last_gps_read       |    ?column?
-------------------------------+------------------+--------------------------+-----------------
 2018-03-21 23:26:07.263931-06 | 1521696367.26393 | 2018-03-21 23:26:00.5273 | 21606.736631155
(1 row)

你应该做什么:

t=# with c("ct", last_gps_read) as (values('2018-03-21 23:26:07.263931-06'::timestamptz,'2018-03-21 23:26:00.5273'::timestamp))
select ct
, extract(EPOCH from ct)
, last_gps_read
, extract(EPOCH from ct - last_gps_read)
from c;
              ct               |    date_part     |      last_gps_read       | date_part
-------------------------------+------------------+--------------------------+-----------
 2018-03-21 23:26:07.263931-06 | 1521696367.26393 | 2018-03-21 23:26:00.5273 |  6.736631
(1 row)

原因:您将double precisiondouble precision分开并获得epoch aware of time zone - epoch not aware of timezone。所以你的结果是预期的。并且documented。我建议做的是使用interval进行时间戳划分,无论是否知道时区(因为区间同时操作),然后从interval中提取时期。这样您就不需要调整时区或将时区与时区统一为AT TIME ZONE 'GTM'或其他......

相关问题