Skip to main content

时区

有四个与 Apache Superset 相关的独立时区组件,

  1. 底层数据编码的时区。
  2. 数据库引擎的时区。
  3. Apache Superset 后端的时区。
  4. Apache Superset 客户端的时区。

如果时间字段(DATETIMETIMETIMESTAMP 等)没有明确定义时区,则默认为组件的底层时区。

为了使问题在某种程度上变得可行——鉴于 Apache Superset 无法控制数据的摄入方式(1)或客户端的时区(4)——从一致性角度来看,强烈建议(2)和(3)都配置为使用相同的时区, 并强烈倾向于使用 UTC,以确保没有明确时间戳的时间字段不会被错误地强制转换为错误的时区。 实际上,Apache Superset 当前隐含假设时间戳处于 UTC 中,因此将(3)配置为非 UTC 时区可能会出现问题。

为了追求数据一致性(无论客户端的时区如何),Apache Superset 后端试图确保发送给客户端的任何时间戳都包含明确的(或者在 Epoch 时间的情况下为半明确,始终参照 UTC)时区内编码。

然而,挑战在于 Apache Superset 支持的一系列数据库引擎及其 Python 数据库 API(DB-API)实现之间的各种不一致, 再加上我们使用 Pandas在序列化为 JSON 之前将 SQL 读取到 DataFrame 中这一事实。 遗憾的是,Pandas 忽略了 DB-APItype_code, 默认依赖于 DB-API 返回的底层 Python 类型。目前,只有部分受支持的数据库引擎能正确与 Pandas 配合工作, 即,确保没有明确时间戳的时间戳以服务器时区序列化为 JSON, 从而保证客户端将以一致的方式显示时间戳,而不管客户端的时区如何。

例如,以下是 MySQL 和 Presto 的比较,

import pandas as pd
from sqlalchemy import create_engine

pd.read_sql_query(
sql="SELECT TIMESTAMP('2022-01-01 00:00:00') AS ts",
con=create_engine("mysql://root@localhost:3360"),
).to_json()

pd.read_sql_query(
sql="SELECT TIMESTAMP '2022-01-01 00:00:00' AS ts",
con=create_engine("presto://localhost:8080"),
).to_json()

分别输出 {"ts":{"0":1640995200000}} (根据 Epoch 时间定义推断出 UTC 时区) 和 {"ts":{"0":"2022-01-01 00:00:00.000"}} (没有明确的时区) 因此在 JavaScript 中被区别对待:

new Date(1640995200000)
> Sat Jan 01 2022 13:00:00 GMT+1300 (New Zealand Daylight Time)

new Date("2022-01-01 00:00:00.000")
> Sat Jan 01 2022 00:00:00 GMT+1300 (New Zealand Daylight Time)