PostgreSQL 时间戳不会将属性转换为 Unix 时间戳

PostgreSQL timestamps do not convert properly into Unixtimestamps

我刚开始使用 PostgreSQL。由于我们使用了很多 Unix 时间戳,我认为最好只尝试一个小例子。因此,我使用 phpPgAdmin 创建了一个 table test,其中包含一个 ID 字段和一个 timestamp without time zone 类型的字段 timestamp。有有效数据。

现在我只想使用 Unix 时间戳检索数据。我 运行 的行为真的很奇怪。

我做了这个小测试,它通过将第一个最佳时间戳转换为 unixtimestamp 然后再转换回时间戳来选择数据:

这将 return 第一个条目的原始和 unix 值中的时间戳

SELECT timestamp, extract(epoch FROM timestamp) 
FROM test 
WHERE id IN (SELECT id FROM test LIMIT 1);

好的,按预期工作。
这只会转换因此它应该 return 一个条目

SELECT timestamp, extract(epoch FROM timestamp) 
FROM test 
WHERE timestamp IN (SELECT TO_TIMESTAMP(extract(epoch FROM timestamp)) 
                    FROM test LIMIT 1); 

没有 returned!

我就是无法理解这种行为。如果我将时间戳转换成其他东西然后再将其转换回来,它应该是相同的时间戳,对吗?

我只是想要一种方法将时间戳转换为 unixtime 并返回,而不会丢失信息。

请注意:这只是一个测试!我不想使用此代码。我只想检查 SQL 代码是否按预期运行! 此外,我无法对 unixtimestamps 做任何事情!我只是想要一种正确投射它们的方法!

不使用不带时区的时间戳类型与带时区的区别:

 select '09/12/2020 11:16 PDT'::timestamp, to_timestamp(extract(epoch from '09/12/2020 11:16 PDT'::timestamp));
      timestamp      |      to_timestamp       
---------------------+-------------------------
 09/12/2020 11:16:00 | 09/12/2020 04:16:00 PDT

select '09/12/2020 11:16 PDT'::timestamptz, to_timestamp(extract(epoch from '09/12/2020 11:16 PDT'::timestamptz));
       timestamptz       |      to_timestamp       
-------------------------+-------------------------
 09/12/2020 11:16:00 PDT | 09/12/2020 11:16:00 PDT

更新。更多详情 see。重要的部分是:

In a literal that has been determined to be timestamp without time zone, PostgreSQL will silently ignore any time zone indication. That is, the resulting value is derived from the date/time fields in the input value, and is not adjusted for time zone.

For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.

When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).

这意味着:

select '09/12/2020 11:16 PDT'::timestamp;
      timestamp      
---------------------
 09/12/2020 11:16:00


select '09/12/2020 11:16'::timestamp at time zone 'UTC';
        timezone         
-------------------------
 09/12/2020 04:16:00 PDT

 select '09/12/2020 11:16 PDT'::timestamp at time zone 'UTC';
        timezone         
-------------------------
 09/12/2020 04:16:00 PDT

select '09/12/2020 11:16 PDT'::timestamptz;
       timestamptz       
-------------------------
 09/12/2020 11:16:00 PDT


select '09/12/2020 11:16 PDT '::timestamptz at time zone 'UTC';
      timezone       
---------------------
 09/12/2020 18:16:00

所以在第一种情况下,时区被忽略,时间被取为09/12/2020 11:16 。在第二种和第三种情况下,时间戳被视为 UTC 并且显示的值被旋转到我的时区 PDT。在第三种情况下,我使用 timestamptz 所以时间戳正确标记了时区。这意味着当我要求在 UTC 显示时,它做了正确的事情。

As documented in the manual to_timestamp() returns a timestamp WITH time zone, 所以显然用 a timestamp WITHOUT time zone 比较是行不通的.

您需要将结果从 to_timestamp() 转换回不带时区的时间戳,方法是告诉 Postgres 它应该采用哪个时区:

SELECT "timestamp", 
       extract(epoch FROM "timestamp"), 
       cast("timestamp" as timestamp with time zone),
       TO_TIMESTAMP(extract(epoch FROM "timestamp")) at time zone 'UTC'
FROM test 
where "timestamp" in (select to_timesatmp(extract(epoch FROM "timestamp")) at time zone 'UTC'
                      from test 
                      limit 1)

这与以下基本相同:

SELECT "timestamp", 
       extract(epoch FROM "timestamp"), 
       cast("timestamp" as timestamp with time zone),
       TO_TIMESTAMP(extract(epoch FROM "timestamp")) at time zone 'UTC'
FROM test 
where "timestamp" in (select "timestamp"
                      from test 
                      limit 1)

但是在数字和适当的 timestamp 之间来回转换的整个过程是无用的 - 如果您混合使用不同的数据类型(正如您刚刚发现的那样),则很容易出错。最好的办法是将所有内容都保持为 timestamp 或更好地保持为 timestamptz.

为了简短介绍为什么 timestamptzwith 时区)` 通常是更好的选择,您可能需要阅读 this