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,函式依實作不同可能回傳 null、Invalid Date、或錯誤的時間戳,而呼叫方若沒有明確的 schema validation,pipeline 會繼續執行,資料對不起來時也不會有 stack trace 可查。
TWSE OpenAPI 的日期欄位尤其容易踩到這個問題,原因有三:
- 多個端點的日期欄位名稱看起來類似(
Date、DividendYear、Month),容易讓工程師假設它們格式相同。 - 民國年與西元年在同一個 JSON response 裡並存(見第四節),沒有欄位命名上的提示。
- 官方 swagger.json 對日期欄位的 schema 僅標
"type": "string"加上一個粗略的中文 description(如Date→「日期」、DividendYear→「股利年度」),無 format、無 example——即使有中文 description,仍無法從 schema 區分這是 ROC 還是 AD、共幾碼。
② 五種格式一覽對照表
以下端點的日期欄位均經實際呼叫驗證(2026-05-19 回傳 HTTP 200,快照日期為 2026-05-18):
| 端點 | 欄位 | 實際格式 | 範例值 | 說明 |
|---|---|---|---|---|
| STOCK_DAY_ALL | Date | ROC 7 碼(YYYMMDD) | 1150518 | 民國 115 年 05 月 18 日 |
| STOCK_DAY | 日期(中文欄位名) | ROC 斜線(YYY/MM/DD) | 115/05/18 | 民國年帶斜線分隔 |
| FMSRFK_ALL | Month | ROC 5 碼月份(YYYMM,3 碼民國年 + 2 碼月) | 11505 | 民國 115 年 05 月,5 碼不帶日 |
| BWIBBU_d | DividendYear | ROC 年份(純數字 3 碼) | 114 | 民國 114 年,無月日 |
| BWIBBU_d | Date | AD 8 碼(YYYYMMDD) | 20260518 | 西元 2026 年 05 月 18 日 |
五種格式差異對應五個獨立的 parse 邏輯需求:
1150518→ 需去掉 offset(民國 +1911)再重組日期115/05/18→ 需以/分隔後再套 offset11505→ 年份 3 碼 + 月份 2 碼,無日,只能產出YYYY-MM114→ 純年份,無月日,只能對應到年度層級資料20260518→ AD 8 碼,無 offset 需求,可直接 parse 為 ISO 8601
③ 民國年 vs 西元年:為什麼同一個 API 集合裡兩種曆法並存
TWSE OpenAPI 的曆法不一致源於資料源的歷史分層。
STOCK_DAY_ALL、STOCK_DAY、FMSRFK_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:
STOCK_DAY_ALL(含Date: "1150518",ROC 7 碼)FMSRFK_ALL(含Month: "11505",ROC 5 碼月份)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
- TWSE T86 欄位命名實況 — silent failure 風險為何容易發生 — T86 欄位名稱與口語不一致的 silent failure 機制
- 台股 = TWSE + TPEx 兩個獨立市場 — TWSE 與 TPEx coverage 差異與 endpoint 對照
- TWSE OpenAPI Swagger:https://openapi.twse.com.tw/
- floviq 盤前推播模板:https://portaly.cc/floviq
修正紀錄
先前版本有以下更正:
- ①③: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
如果這篇對你有用: