floviq logo floviq
← 筆記 · 2026-05-19 · mechanics

TWSE OpenAPI 的日期格式陷阱:同一個台股 API 有五種不同格式,整合時的靜默 Bug 溫床

工程師串接 TWSE OpenAPI 多個端點時,常遇到一類不報錯、只回傳空值的問題:日期格式假設錯誤。TWSE OpenAPI 在 openapi.twse.com.tw/v1/exchangeReport/ 路徑下有多個端點,各自使用不同的日期字串格式,且 官方 swagger.json 中每個日期欄位雖帶有粗略的中文 description,卻沒有任何欄位標註 format 或 example。整合時若假設格式統一,parse 邏輯不會 throw error,只會靜默產出錯誤結果。


① 為什麼日期格式錯誤會造成靜默 Bug

日期格式錯誤屬於典型的靜默失敗(silent failure):parse 函式的輸入是一個合法字串,但不符合預期的 format pattern,函式依實作不同可能回傳 nullInvalid Date、或錯誤的時間戳,而呼叫方若沒有明確的 schema validation,pipeline 會繼續執行,資料對不起來時也不會有 stack trace 可查。

TWSE OpenAPI 的日期欄位尤其容易踩到這個問題,原因有三:

  1. 多個端點的日期欄位名稱看起來類似(DateDividendYearMonth),容易讓工程師假設它們格式相同。
  2. 民國年與西元年在同一個 JSON response 裡並存(見第四節),沒有欄位命名上的提示。
  3. 官方 swagger.json 對日期欄位的 schema 僅標 "type": "string" 加上一個粗略的中文 description(如 Date→「日期」、DividendYear→「股利年度」),無 format、無 example——即使有中文 description,仍無法從 schema 區分這是 ROC 還是 AD、共幾碼。

② 五種格式一覽對照表

以下端點的日期欄位均經實際呼叫驗證(2026-05-19 回傳 HTTP 200,快照日期為 2026-05-18):

端點欄位實際格式範例值說明
STOCK_DAY_ALLDateROC 7 碼(YYYMMDD1150518民國 115 年 05 月 18 日
STOCK_DAY日期(中文欄位名)ROC 斜線(YYY/MM/DD115/05/18民國年帶斜線分隔
FMSRFK_ALLMonthROC 5 碼月份(YYYMM,3 碼民國年 + 2 碼月)11505民國 115 年 05 月,5 碼不帶日
BWIBBU_dDividendYearROC 年份(純數字 3 碼)114民國 114 年,無月日
BWIBBU_dDateAD 8 碼(YYYYMMDD20260518西元 2026 年 05 月 18 日

五種格式差異對應五個獨立的 parse 邏輯需求:

  • 1150518 → 需去掉 offset(民國 +1911)再重組日期
  • 115/05/18 → 需以 / 分隔後再套 offset
  • 11505 → 年份 3 碼 + 月份 2 碼,無日,只能產出 YYYY-MM
  • 114 → 純年份,無月日,只能對應到年度層級資料
  • 20260518 → AD 8 碼,無 offset 需求,可直接 parse 為 ISO 8601

③ 民國年 vs 西元年:為什麼同一個 API 集合裡兩種曆法並存

TWSE OpenAPI 的曆法不一致源於資料源的歷史分層。

STOCK_DAY_ALLSTOCK_DAYFMSRFK_ALL 這類交易資料端點的日期欄位沿用 TWSE 內部報表的民國年格式,因為這些端點的底層資料最初是為台灣本地的政府報表與歸檔系統設計的,民國年是法規與會計系統的預設曆法。

BWIBBU_d 是曆法混用情況最顯著的端點:同一個 JSON response 物件裡同時出現 3 種日期表示

  • Date: "20260518" — AD 8 碼(YYYYMMDD),代表資料日期,使用西元年
  • DividendYear: "114" — 民國年(3 碼純數字),對應股利發放的財年(114 = 2025 AD)
  • FiscalYearQuarter: "2026Q1" — 西元年 + 季度,對應財務報表季度標示

三個欄位來自不同資料來源的拼接:資料日期走西元年,股利資料沿用民國年,財報季度資料採用西元年(符合 IFRS / 外資閱讀需求)。三個系統在同一個 response 裡合併輸出,曆法各自保留,沒有統一轉換。Date 欄位與 DividendYear 欄位的命名看起來都是「日期」,但一個是 AD 8 碼、一個是 ROC 3 碼,用同一個 parse 函式處理必然出錯。

官方 swagger.json 對這三個欄位的 schema 只標 "type": "string" 加上一個中文 description(Date→「日期」、DividendYear→「股利年度」、FiscalYearQuarter→「財報年/季」),無 format、無 example——因此光看 schema 無法分辨 Date 是 AD 8 碼、DividendYear 是 ROC 3 碼,兩者一律被標成 string


④ 實際踩坑情境:多端點 workflow 整合時的日期 transform 必要性

典型的多端點整合情境:工程師要把某支股票當日收盤、月成交資料、以及殖利率資料合併進同一筆 record,需要同時 GET:

  1. STOCK_DAY_ALL(含 Date: "1150518",ROC 7 碼)
  2. FMSRFK_ALL(含 Month: "11505",ROC 5 碼月份)
  3. BWIBBU_d(同一物件含 Date: "20260518"DividendYear: "114"FiscalYearQuarter: "2026Q1" 三種格式)

若 pipeline 用同一個 parseDate(str) 函式處理這些日期欄位,結果會是:

  • 1150518 → 大部分 date library 會 parse 失敗或產出錯誤日期(西元元年 + offset)
  • 11505 → 5 碼格式不含日,new Date("11505") 產出無意義結果
  • 2026Q1 → 需要拆 quarter 轉換,new Date("2026Q1") 會是 Invalid Date

這些情況不會讓 workflow 崩潰,但下游的日期比較、排序、JOIN 邏輯會靜默產出錯誤結果。排查時的困難在於:資料本身「看起來有值」,只有在人工交叉比對時才會發現對不起來。


⑤ 安全的日期處理 pattern

不可假設格式統一。每個端點的日期欄位需要獨立的 transform 函式。以下是針對 TWSE OpenAPI 常見格式的 Python 範例:

def parse_twse_roc7(date_str: str) -> str:
    """
    STOCK_DAY_ALL Date 欄位格式: ROC 7 碼 YYYMMDD
    例: '1150518' → '2026-05-18'
    """
    roc_year = int(date_str[:3])
    month = date_str[3:5]
    day = date_str[5:7]
    ad_year = roc_year + 1911
    return f"{ad_year}-{month}-{day}"


def parse_twse_roc_slash(date_str: str) -> str:
    """
    STOCK_DAY 日期欄位格式: ROC 斜線 YYY/MM/DD
    例: '115/05/18' → '2026-05-18'
    """
    parts = date_str.split("/")
    ad_year = int(parts[0]) + 1911
    return f"{ad_year}-{parts[1]}-{parts[2]}"


def parse_twse_roc_month(month_str: str) -> str:
    """
    FMSRFK_ALL Month 欄位格式: ROC 5 碼 YYYMM
    例: '11505' → '2026-05'
    """
    roc_year = int(month_str[:3])
    month = month_str[3:5]
    ad_year = roc_year + 1911
    return f"{ad_year}-{month}"

注意事項:

  • DividendYear: "114" 是民國年份,轉西元只需 int("114") + 1911 = 2025,代表「2025 年度的股利資料」,不代表當前年度
  • FiscalYearQuarter: "2026Q1" 是西元年,不需 offset 轉換,但需拆 quarter 才能對應到具體日期區間
  • 任何 transform 函式輸入前建議先做長度 / 格式 assertion,避免 TWSE 端點日後改格式時靜默轉出錯誤值

See also


修正紀錄

先前版本有以下更正:

  • ①③:swagger.json 對日期欄位並非「無說明」,而是帶粗略中文 description,但無 format、無 example。
  • ②:FMSRFK_ALL Month 格式 4 碼年 + 1 碼月YYYMM(3 碼民國年 + 2 碼月)。

※ 本文描述 TWSE OpenAPI 日期欄位格式差異與多端點整合的技術機制 · 不構成投資建議,投資請自行評估風險。

資料來源:TWSE STOCK_DAY_ALL · TWSE STOCK_DAY · TWSE FMSRFK_ALL · TWSE BWIBBU_d · TWSE OpenAPI swagger.json