Redshift - 将时区偏移量(Varchar)添加到时间戳列

Posted

技术标签:

【中文标题】Redshift - 将时区偏移量(Varchar)添加到时间戳列【英文标题】:Redshift - Adding timezone offset (Varchar) to timestamp column 【发布时间】:2019-03-12 18:27:28 【问题描述】:

作为 ETL 到 Redshift 的一部分,在其中一个源表中,有 2 列: original_timestamp - TIMESTAMP: 记录插入到哪个区域的当地时间 original_timezone_offset - Varchar: 与 UTC 的偏移量

数据看起来像这样:

original_timestamp original_timezone_offset
2011-06-22 11:00:00.000000 -0700
2014-11-29 17:00:00.000000 -0800
2014-12-02 22:00:00.000000 +0900
2011-06-03 09:23:00.000000 -0700
2011-07-28 03:00:00.000000 -0700
2011-05-01 01:30:00.000000 -0700

在我的目标表中,我需要将其转换为 UTC(使用偏移量)。我该怎么做? 到目前为止,我已经尝试了多种方法,但 dateadd() 似乎是最接近的解决方案。但是dateadd() 的问题是,当我说:

SELECT original_timestamp, original_timezone_offset
 ,dateadd(H, original_timezone_offset, original_timestamp) as original_utc_time

它在原始时间戳中添加/减去 '700'/'800' 小时而不是 7/8 小时,因为偏移量是 VARCHAR 并且值类似于:-0700 等。

以前有人见过这个问题吗?感谢任何帮助/输入。谢谢。

【问题讨论】:

是偏移列文本吗? 是的,约翰。这是一个文本 【参考方案1】:

只取偏移量的“小时”部分:

WITH t as (
SELECT  '2011-06-22 11:00:00.000000'::timestamp as original_timestamp, '-0700' as original_timezone_offset
UNION ALL
SELECT '2014-11-29 17:00:00.000000'::timestamp,'-0800'
UNION ALL
SELECT '2014-12-02 22:00:00.000000'::timestamp,'+0900'
)
SELECT
  original_timestamp,
  original_timezone_offset,
  DATEADD(hour, SUBSTRING(original_timezone_offset, 1, 3)::INT, original_timestamp)
FROM t

2011-06-22 11:00:00 -0700   2011-06-22 04:00:00
2014-11-29 17:00:00 -0800   2014-11-29 09:00:00
2014-12-02 22:00:00 +0900   2014-12-03 07:00:00

如果您有非全时偏移(例如 +0730),您将需要一些额外的花哨代码。

【讨论】:

【参考方案2】:

首先,认识到如果您的时间戳已经在给定偏移的本地时间,那么您需要减去该偏移以转换回 UTC。在您给出的第一个示例中,2011-06-22 11:00:00 -0700 等同于 2011-06-22 18:00:00 UTC

但是,与其尝试自己添加或减去这些值,不如让AT TIME ZONE 函数为您完成这项工作。它将在您提供的偏移量中创建一个 timestamptz,然后您可以再次使用它来转换为 UTC。

(请注意,您可以改用 CONVERT_TIMEZONE 函数,但只有 Redshift 可以理解,其中 AT TIME ZONE 也适用于常规 PostgreSQL。)

但是,您所拥有的时区偏移量不是这些函数能够理解的格式。见time zone usage notes。因此,在我们尝试转换之前,让我们将您的偏移字符串转换为可理解的格式。

我们希望-0700 变成+07:00。冒号是必需的,并且必须翻转符号,因为它将使用 POSIX 样式的时区格式进行解释。在该格式中,正值位于 GMT west 而非 ISO 8601 中指定的通常约定。

concat(translate(substring(original_timezone_offset, 1, 3), '-+', '+-'),':',substring(original_timezone_offset, 4, 2))

然后我们将它与AT TIME ZONE 一起使用来进行转换:

(original_timezone AT TIME ZONE <the above mess>) AT TIME ZONE 'UTC' AS utc_timestamp

把它们放在一起......

WITH t as (
SELECT  '2011-06-22 11:00:00.000000'::timestamp as original_timestamp, '-0700' as original_timezone_offset
UNION ALL
SELECT '2014-11-29 17:00:00.000000'::timestamp,'-0800'
UNION ALL
SELECT '2014-12-02 22:00:00.000000'::timestamp,'+0900'
)
SELECT
  original_timestamp,
  original_timezone_offset,
  concat(translate(substring(original_timezone_offset, 1, 3), '-+', '+-'),':',substring(original_timezone_offset, 4, 2)) as modified_timezone_offset,
  (original_timestamp AT TIME ZONE concat(translate(substring(original_timezone_offset, 1, 3), '-+', '+-'),':',substring(original_timezone_offset, 4, 2))) AT TIME ZONE 'UTC' AS utc_timestamptz
FROM t

输出:

2011-06-22 11:00:00  -0700  +07:00  2011-06-22 18:00:00
2014-11-29 17:00:00  -0800  +08:00  2014-11-30 01:00:00
2014-12-02 22:00:00  +0900  -09:00  2014-12-02 13:00:00

SQL Fiddle here.

【讨论】:

以上是关于Redshift - 将时区偏移量(Varchar)添加到时间戳列的主要内容,如果未能解决你的问题,请参考以下文章

.NET 通过时区名称获取时区偏移量

时区分钟偏移量的 TimeZoneInfo

使用时区偏移量而不是时区标识符将 GMT 时间转换为本地时间

将时区偏移量(ISO 8601 格式)添加到原始日期时间

获取节点中特定时区的 UTC 偏移量和 DST 信息? [复制]

Python pytz 时区转换返回的值与不同日期的时区偏移量不同