跳至主要内容
在 GitHub 上编辑此页面

时区

有四个不同的时区组件与 Apache Superset 相关联,

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

如果时间字段(`DATETIME`、`TIME`、`TIMESTAMP` 等)没有明确定义时区,则默认使用该组件的底层时区。

为了帮助解决这个问题(考虑到 Apache Superset 无法控制数据的摄入方式(1)或客户端的时区(4)),从一致性的角度来看,强烈建议将(2)和(3)配置为使用相同的时区,优先使用 UTC,以确保没有明确时间戳的时间字段不会被错误地转换为错误的时区。实际上,Apache Superset 目前隐式地假设时间戳为 UTC,因此将(3)配置为非 UTC 时区可能会导致问题。

为了追求数据一致性(无论客户端的时区如何),Apache Superset 后端试图确保发送给客户端的任何时间戳都包含一个明确的(或半明确的,如 Epoch 时间,它始终与 UTC 相关)时区编码。

然而,挑战在于 Apache Superset 支持的大量 数据库引擎 以及它们的 Python 数据库 API (DB-API) 实现之间存在的各种不一致性,再加上我们使用 Pandas 将 SQL 读取到 DataFrame 中,然后再序列化为 JSON。遗憾的是,Pandas 忽略了 DB-API 的 type_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://127.0.0.1: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)