InfluxDB 3.0 中改进的时区支持

导航至

我们在 InfluxDB 3.0 SQL 中添加了一个新函数 tz,以提高处理时区的效率。这篇博文将更详细地介绍如何使用这个函数,以及何时使用它来代替现有的 SQL 时区函数。

tz 函数

新的 tz 函数的目标是始终如一地简化各种时区之间的时间转换,用于相对于 Unix 纪元的时间点。这与 at time zone 语法形成对比,后者根据输入数据的不同提供不同的语义。有关两者之间差异的更详细解释,请参阅“什么是时间”部分。

用法

tz 函数接受一个或两个参数。第一个参数始终是纳秒时间戳,可以带有任何时区或不带时区。第二个参数是有效的时区字符串。如果未提供,则默认为 UTC。

以下是如何使用 tz 的一些示例

-- Convert from un-timezoned to 'Europe/Brussels'
SELECT tz('2024-04-01T00:00:20', 'Europe/Brussels') as t;
+---------------------------+
| t                         |
+---------------------------+
| 2024-04-01T02:00:20+02:00 |
+---------------------------+

-- Convert from un-timezoned to UTC
SELECT tz('2024-04-01T00:00:20') as t, arrow_typeof(tz('2024-04-01T00:00:20')) as type;
+----------------------+------------------------------------+
| t                    | type                               |
+----------------------+------------------------------------+
| 2024-04-01T00:00:20Z | Timestamp(Nanosecond, Some("UTC")) |
+----------------------+------------------------------------+

-- Convert from un-timezoned to America/New_York and then to America/Denver
SELECT tz(tz('2024-04-01T00:00:20', 'America/New_York'), 'America/Denver') as t;
+---------------------------+
| t                         |
+---------------------------+
| 2024-03-31T18:00:20-06:00 |
+---------------------------+

需要注意的一点是,tz 的返回类型将始终与时区关联,即使该时区是 UTC。这对于像 Python(和其他语言)这样的语言很重要,在这些语言中,不支持比较 tz-naivetz-aware 类型。任何使用此功能的客户端代码都需要准备好处理时区感知数据。

SELECT arrow_typeof(tz('2024-04-01T00:00:20'));
+-----------------------------------------------+
| arrow_typeof(tz(Utf8("2024-04-01T00:00:20"))) |
+-----------------------------------------------+
| Timestamp(Nanosecond, Some("UTC"))            |
+-----------------------------------------------+

什么是时间

为了解释这种变化,将给定的时间视为“绝对”时间或“日历”时间是有用的。

绝对时间是相对于 UTC 纪元的时间。例如,时间 2024-08-01T06:00:00Z 是 1722492000 纪元秒,这等效于 2024-08-01T00:00:00-06:00(美国/丹佛时区)。

日历时间是您在给定时区的时钟上实际看到的时间。例如,您可能想要 2024-07-17T17:00:00 在美国/丹佛时区那一刻的时间,因此您的时间将表示为 2024-07-17T17:00:00-06:00(1721257200 纪元秒)。

这两个概念之间的区别通过 tzat time zone 来例证。

让我们看一个简单的例子

SELECT '2024-04-01T00:00:20Z'::timestamp AT TIME ZONE 'Europe/Brussels';
+------------------------------+
| Utf8("2024-04-01T00:00:20Z") |
+------------------------------+
| 2024-04-01T00:00:20+02:00    |
+------------------------------+

SELECT tz('2024-04-01T00:00:20', 'Europe/Brussels');
+---------------------------------------------------------+
| tz(Utf8("2024-04-01T00:00:20"),Utf8("Europe/Brussels")) |
+---------------------------------------------------------+
| 2024-04-01T02:00:20+02:00                               |
+---------------------------------------------------------+

当时间戳没有时区(InfluxDB 3.0 中的默认行为)时,at time zone 转换返回指定时区的日历时间。当您在“where”子句中设置时间范围,并且您希望它们基于特定时区(通常是您的本地时间)时,此函数非常有用。

例如,您想要将查询限制为最后一天,日期从美国/纽约边界开始

select * from my_cool_stuff where time > '2024-01-01' at time zone 'America/New_York' and time < '2024-01-02' at time zone 'America/New_York'

tz 函数始终返回指定时区的绝对时间。当您有已写入 InfluxDB 的现有数据(始终是 UTC 根据线路协议规范),并且您想要在同一时间点显示它,但在本地时区显示时,这非常有用。

例如,您有 Telegraf 将您的内存使用情况写入数据库。您可以使用 tz 函数,而无需在心理上将偏移量应用于每个值

select
    time,
    time at time zone 'America/New_York' as time_atz,
    tz(time, 'America/New_York') as time_tz 
from mem order by time limit 3;
+----------------------+---------------------------+---------------------------+
| time                 | time_atz                  | time_tz                   |
+----------------------+---------------------------+---------------------------+
| 2020-06-11T16:51:50Z | 2020-06-11T16:51:50-04:00 | 2020-06-11T12:51:50-04:00 |
| 2020-06-11T16:52:00Z | 2020-06-11T16:52:00-04:00 | 2020-06-11T12:52:00-04:00 |
| 2020-06-11T16:52:10Z | 2020-06-11T16:52:10-04:00 | 2020-06-11T12:52:10-04:00 |
+----------------------+---------------------------+---------------------------+

在上面的示例中,time_atz 并不准确地表示数据写入的时间。它有效地将点向前移动了 4 个小时。time_tz 提供了所需的行为,即看到记录点的固定时间点,但在不同的时区显示。