現代應用常以 Unix Epoch 毫秒數(自 1970-01-01 00:00:00 UTC)傳遞時間戳記,Oracle 需正確雙向轉換並處理時區。本文提供標準 SQL 轉換式、時區調整、效能考量與常見陷阱。
1970-01-01 00:00:00 UTCDATE '1970-01-01' + (seconds / 86400)(Oracle DATE 以「天」為單位,1 天 = 86400 秒)-- 參數:1406538765000 (毫秒)
SELECT TO_CHAR(
DATE '1970-01-01'
+ (1406538765000 / 1000 / 86400)
+ (8/24), -- UTC+8 時區調整
'YYYY-MM-DD HH24:MI:SS'
) AS local_datetime
FROM dual;
-- 結果:2014-07-28 17:12:45
-- 參數:'2014-07-28 17:12:45' (假設為 UTC+8 本地時間)
SELECT ROUND(
(TO_DATE('2014-07-28 17:12:45', 'YYYY-MM-DD HH24:MI:SS')
- DATE '1970-01-01'
- (8/24)) -- 扣除 UTC+8 差值,回歸 UTC
) * 86400 * 1000
AS epoch_millis
FROM dual;
-- 結果:1406538765000
-- 回傳 UTC 毫秒數
SELECT ROUND(
(SYSTIMESTAMP - TIMESTAMP '1970-01-01 00:00:00 UTC')
) * 86400 * 1000 AS utc_millis
FROM dual;
-- 或使用 DBMS_UTILITY.GET_TIME (百分秒) * 10
SELECT DBMS_UTILITY.GET_TIME * 10 AS centisec_millis FROM dual;
CREATE OR REPLACE FUNCTION epoch_ms_to_date (p_ms IN NUMBER, p_tz_offset_hours IN NUMBER DEFAULT 8)
RETURN DATE DETERMINISTIC IS
BEGIN
RETURN DATE '1970-01-01' + (p_ms / 1000 / 86400) + (p_tz_offset_hours / 24);
END;
/
CREATE OR REPLACE FUNCTION date_to_epoch_ms (p_dt IN DATE, p_tz_offset_hours IN NUMBER DEFAULT 8)
RETURN NUMBER DETERMINISTIC IS
BEGIN
RETURN ROUND((p_dt - DATE '1970-01-01' - (p_tz_offset_hours / 24)) * 86400 * 1000);
END;
/
-- 使用範例
SELECT epoch_ms_to_date(1406538765000, 8) FROM dual; -- 2014-07-28 17:12:45
SELECT date_to_epoch_ms(DATE '2014-07-28' + 17/24 + 12/1440 + 45/86400, 8) FROM dual; -- 1406538765000
-- 使用 TIMESTAMP WITH TIME ZONE 避免手動時區計算
SELECT
TO_CHAR(
FROM_TZ(CAST(DATE '1970-01-01' AS TIMESTAMP), 'UTC')
+ NUMTODSINTERVAL(1406538765000/1000, 'SECOND')
AT TIME ZONE 'Asia/Taipei',
'YYYY-MM-DD HH24:MI:SS TZR'
) AS tz_aware
FROM dual;
-- 反向
SELECT EXTRACT(DAY FROM (CAST(TO_TIMESTAMP_TZ('2014-07-28 17:12:45 Asia/Taipei', 'YYYY-MM-DD HH24:MI:SS TZR') AS DATE) - DATE '1970-01-01')) * 86400 * 1000
-- 注意:需先轉為 UTC 再計算,建議用函數封裝
| 陷阱 | 症狀 | 修正 |
|---|---|---|
| 忽略時區 | 轉換結果相差 8 小時 (或其他時差) | 明確指定來源/目標時區,統一用 UTC 儲存,顯示時再轉 |
| 用 DATE 型別儲存毫秒精度 | DATE 精度只到秒,毫秒截斷 | 需毫秒精度請用 TIMESTAMP(3) 或 NUMBER 儲存 Epoch 毫秒 |
| 整數除法截斷 | PL/SQL 中 ms/1000 若 ms 為 NUMBER 整數,結果仍為 NUMBER 但可能精度問題 | 明確轉型 ms/1000.0 或 CAST(ms AS FLOAT)/1000 |
| 夏令時間 (DST) 未處理 | 特定日期轉換錯誤 1 小時 | 使用 TIMESTAMP WITH TIME ZONE + AT TIME ZONE 自動處理 DST |
SELECT ... FROM big_table 直接在 SELECT 列表計算)。EPOCH_MILLIS NUMBER,建立索引,查詢 WHERE epoch_millis > :cutoff 最快。標準化 Epoch 毫秒轉換邏輯,統一以 UTC 儲存、應用層處理顯示時區,是現代分散式系統時間一致性的基石。