時區
與 Apache Superset 相關的有四個不同的時區組件:
- 基礎資料編碼所使用的時區。
- 資料庫引擎的時區。
- Apache Superset 後端的時區。
- 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)