【问题标题】:Converting local datetime values to UTC datetime values in SQL Server and PostgreSQL在 SQL Server 和 PostgreSQL 中将本地日期时间值转换为 UTC 日期时间值
【发布时间】:2021-09-28 05:02:34
【问题描述】:

我正在尝试了解 AT TIME ZONE 的行为,但在这方面我发现 SQL Server 和 PostgreSQL 之间存在一些奇怪的行为差异。

出于演示目的,我生成了两个查询,一个用于 SQL Server,一个用于 PostgreSQL。

SQL Server 查询:

SELECT
  [LocalTime],
  [LocalTime] AT TIME ZONE 'W. Europe Standard Time' AS [LocalTimeWithOffset],
  ([LocalTime] AT TIME ZONE 'W. Europe Standard Time') AT TIME ZONE 'UTC' AS [UniversalTime]
FROM
  (VALUES
    (CAST('2020-06-20 12:34:56.789' AS DATETIME2)),
    (CAST('2020-12-20 12:34:56.789' AS DATETIME2))) AS [X] ([LocalTime])

产生这个结果(在 SQL Server Management Studio 中):

LocalTime                    LocalTimeWithOffset                 UniversalTime
---------                    -------------------                 ----------------
2020-06-20 12:34:56.7890000  2020-06-20 12:34:56.7890000 +02:00  2020-06-20 10:34:56.7890000 +00:00
2020-12-20 12:34:56.7890000  2020-12-20 12:34:56.7890000 +01:00  2020-12-20 11:34:56.7890000 +00:00

这对我来说似乎很好,因为我住在荷兰:在夏季,我们比 UTC 早 2 小时(由于夏令时),而在冬季,我们只比 UTC 早 1 小时。

但是... PostgreSQL 查询:

SELECT
  "LocalTime",
  "LocalTime" AT TIME ZONE 'WET' AS "LocalTimeWithOffset",
  ("LocalTime" AT TIME ZONE 'WET') AT TIME ZONE 'UTC' AS "UniversalTime"
FROM
  (VALUES
    (timestamp '2020-06-20 12:34:56.789'),
    (timestamp '2020-12-20 12:34:56.789')) AS "X" ("LocalTime")

产生以下结果(在 pgAdmin 中):

LocalTime                    LocalTimeWithOffset         UniversalTime
timestamp without time zone  timestamp with time zone    timestamp without time zone
---------------------------  ------------------------    ---------------------------
2020-06-20 12:34:56.789      2020-06-20 14:34:56.789+02  2020-06-20 12:34:56.789
2020-12-20 12:34:56.789      2020-12-20 13:34:56.789+01  2020-12-20 12:34:56.789

这对我来说似乎不正确。

在为此苦苦挣扎了一会儿之后,我想出了另一个 PostgreSQL 查询,它似乎产生了与 SQL Server 查询相同的结果:

SELECT
  "LocalTime",
  "LocalTime"::timestamptz AS "LocalTimeWithOffset",
  "LocalTime"::timestamptz AT TIME ZONE 'UTC' AS "UniversalTime"
FROM
  (VALUES
    (timestamp '2020-06-20 12:34:56.789'),
    (timestamp '2020-12-20 12:34:56.789')) AS "X" ("LocalTime")

但是这个查询似乎依赖于 PostgreSQL 服务器的时区。 :-(

是否有任何(最好是优雅的)方法来模拟 PostgreSQL 中的 SQL Server 逻辑/行为,而不依赖于服务器的时区(并在查询中使用明确指定的时区)?

编辑

我知道将 UTC 日期/时间值存储在数据库中并在呈现给最终用户时将它们(最好在客户端中/由客户端)转换为本地日期/时间值是一种很好的做法。当然,我也打算这样做,但我目前的情况是从 SQL Server 到 PostgreSQL 的数据库迁移(作为大型应用程序大修的一部分),其中 SQL Server 数据库中的 DATETIME2 表列包含本地日期/time 值,我想将其转换为 PostgreSQL 表中相应 timestamp 列中的 UTC 日期/时间值。我的迁移脚本可以很好地处理上面的查询,所以实际上我没有阻塞问题。提出这个问题的原因是,我只是对 SQL Server 和 PostgreSQL 在 AT TIME ZONE 行为方面的明显差异以及克服这些行为差异的方法感到好奇。

【问题讨论】:

  • 您的timezone 设置是什么?
  • @LaurenzAlbe 。 . .在 PostgreSQL 中执行 SHOW TIMEZONE 会得到值 CET。我猜那不是正确的时区...
  • 嗯,timestamp with timezone 的显示取决于您对timezone 的设置。
  • @LaurenzAlbe 。 . .是啊。我想我知道你的意思。 timestamp with time zone 类型的值不包含时区信息。这些值只是在内部存储为 UTC 日期/时间值。例如,当执行查询SELECT timestamptz '2020-12-20 12:00:00+02', timestamptz '2020-12-20 14:00:00+04' 时,我会得到值2020-12-20 11:00:00+012020-12-20 11:00:00+01。与 SQL Server 的 DATETIMEOFFSET 数据类型相比,行为完全不同。我将对此进行更多研究。也许更改 TIMEZONE 设置会使我的最后一个查询“可配置”。
  • 我会说timestamp with timezone 在 PostgreSQL 中的工作方式与在其他数据库中不同。我怀疑试图让他们表现得一样会失败,你必须有更广阔的视野。

标签: postgresql datetime-conversion postgresql-13


【解决方案1】:

在 postgresql 中,AT TIME ZONE 允许将时区添加到缺少它们的日期/时间值(TIMESTAMP WITHOUT TIME ZONE,::timestamp),并允许将 TIMESTAMP WITH TIME ZONE 值(::timestamptz)转换为非本地时区和时区名称已删除。

因此,在您为 postgres 编写的第一个查询中,您尝试将'2020-06-20 12:34:56.789' 转换为时间戳,并且您已将AT TIME ZONE 应用于LocalTimeWithOffset。这是 postgres 将输入视为 UTC 时间戳并转换为相应时区的情况,在您的情况下为 WET,在夏季为 UTC+2:00,在冬季为 UTC+1:00

现在,在第三种情况下,您首先将AT TIME ZONE 'WET' 应用于时间戳'2020-06-20 12:34:56.789',因此输入被视为UTC 并转换为WET,输出为timstamptz,而这然后输出再次馈送到AT TIME ZONE 'UTC',因此根据AT TIME ZONE 的行为,它被转换为UTC,并且时区标签被删除,也就是我们给出的输入。

因此,根据documentation.postgresql 中的 AT TIME ZONE 并没有表现得很奇怪,而是完全按照它应该做的事情来做。

希望您的困惑已消除。

【讨论】:

  • 嗯。我仍然很困惑。我确实理解您的解释,但我不明白为什么在我的第一个查询的第二列中应用 AT TIME ZONE 会假定时间戳的输入是 UTC ......这只是一个 timestamp 值,我想表明它是WET 时区值,而不是 UTC 时区值。我应该如何做到这一点? (正如您在第一段中所说,在 PostgreSQL 中,AT TIME ZONE 应该允许将时区添加到缺少它们的日期/时间值(TIMESTAMP WITHOUT TIME ZONE, ::timestamp)。据我所知,这就是案例在这里。)
猜你喜欢
  • 1970-01-01
  • 2012-10-07
  • 1970-01-01
  • 2011-08-29
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 2011-05-06
相关资源
最近更新 更多