WebberUI

民國年日期選擇器(React)

民國/西元一鍵切換的 React 日期選擇器:年月日格狀月曆+「民國 115 年」年份下拉,可直接輸入「115/08/18」或「2026-08-18」文字解析(含 IME 檢查),輸出 ISO 日期並同時回傳民國與西元兩種寫法。

台灣表單(生日、證件效期、保單)習慣用民國年,但後端要的是西元 ISO 字串。這個元件把兩者接起來:文字輸入框可直接打「115/08/18」「2026-08-18」「民國115年8月18日」甚至「1150818」,失焦或按 Enter 就解析(中文輸入法組字中的 Enter 不會誤觸發);右側按鈕開啟月曆彈層,頂部「民國|西元」segmented 切換時年份標籤即時換算(民國=西元−1911,民國前的年份直接顯示西元不換算),搭配年份下拉、月份箭頭、7 欄格狀日期、今天標記與 minmax 停用範圍。value 永遠是 ISO 字串(YYYY-MM-DD),onChange 同時給民國、西元與依當前曆制格式化的顯示字串。

載入預覽⋯
npx shadcn@latest add https://webberui.com/r/roc-date-picker.json

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

<RocDatePicker />

安裝

npx shadcn@latest add https://webberui.com/r/roc-date-picker.json

或在 components.json 設定 registries 後,改用 @webberui/roc-date-picker 安裝。

使用

import {
  RocDatePicker,
  formatRocDate,
  parseRocDateInput,
} from "@/components/ui/roc-date-picker";

const [iso, setIso] = React.useState("2026-08-18");

<RocDatePicker
  value={iso}
  onChange={(next, meta) => {
    setIso(next); // "2026-08-18";清除時為 ""
    if (meta) {
      console.log(meta.roc); // { year: 115, month: 8, day: 18 }
      console.log(meta.gregorian); // { year: 2026, month: 8, day: 18 }
      console.log(meta.display); // 民國模式 "115/08/18"、西元模式 "2026/08/18"
    }
  }}
  label="保單生效日"
  min="2026-01-01"
  max="2027-12-31"
/>;

// 解析與格式化函式可單獨使用(例如匯入舊資料、列印時顯示民國年)
parseRocDateInput("115/08/18"); // "2026-08-18"
parseRocDateInput("民國115年8月18日"); // "2026-08-18"
parseRocDateInput("2026-08-18"); // "2026-08-18"
formatRocDate("2026-08-18", "roc"); // "115/08/18"
formatRocDate("2026-08-18", "gregorian", "-"); // "2026-08-18"

Props

Prop型別預設值說明
valuestring | null受控值:ISO 日期字串(YYYY-MM-DD),空字串或 null 表示未選;不提供時為非受控模式
defaultValuestring""非受控模式的初始值(ISO 字串)
onChange(iso: string, meta: RocDateChangeMeta | null) => void值變動時回呼:第一參數為 ISO 字串(清除時為空字串),第二參數同時給民國、西元與顯示字串(清除時為 null
calendar"roc" | "gregorian"受控曆制:民國(roc)或西元(gregorian);不提供時由元件內部的切換鈕管理
defaultCalendar"roc" | "gregorian""roc"非受控模式的初始曆制
onCalendarChange(calendar: RocCalendar) => void使用者按下「民國|西元」切換時回呼
weekStartsOn0 | 10一週從週日(0)或週一(1)開始
minstring可選的最早日期(ISO 字串,含當日);更早的日期停用、輸入時視為超出範圍
maxstring可選的最晚日期(ISO 字串,含當日);更晚的日期停用、輸入時視為超出範圍
labelstring"日期"欄位標籤文字
placeholderstring輸入框佔位文字;未提供時依曆制顯示「例:115/08/18」或「例:2026/08/18」
disabledbooleanfalse是否停用
namestring表單欄位名稱:以隱藏 input 送出 ISO 字串(顯示用的輸入框不帶 name)
classNamestring透傳到最外層容器

RocDateChangeMeta

欄位型別預設值說明
rocRocDate民國年月日(西元 1911 年以前的日期 year 會是 0 或負數)
gregorianRocDate西元年月日
displaystring依當前曆制格式化的顯示字串(「115/08/18」或「2026/08/18」)

RocDate

欄位型別預設值說明
yearnumber年(依上下文為民國年或西元年)
monthnumber月(1~12)
daynumber日(1~31)

具名匯出的函式

  • parseRocDateInput(raw: string): string | null——把使用者輸入的文字解析為 ISO 字串,失敗回傳 null(規則見下方「細節」)
  • formatRocDate(iso: string, calendar?: RocCalendar, separator?: string): string——依曆制格式化 ISO 日期,預設民國模式、分隔符「/」;ISO 不合法回傳 ""
  • toRocDate(iso: string): RocDate | null——ISO 字串轉民國年月日
  • fromRocDate(roc: RocDate): string | null——民國年月日轉 ISO 字串;日期不存在(如 2 月 30 日)回傳 null

另外匯出 RocCalendar"roc" | "gregorian")型別,方便在表單層共用同一套換算邏輯。

細節

  • 文字解析規則:接受「115/08/18」「115-8-18」「115.08.18」「民國115年8月18日」「1150818」與「2026-08-18」「2026/08/18」「2026年8月18日」「20260818」,全形數字與全形分隔符會先正規化。年份大於等於 1000 視為西元,否則視為民國(+1911);帶「民國」前綴一律視為民國年;8 碼純數字視為西元 YYYYMMDD,6~7 碼視為民國 (Y)YYMMDD
  • 解析失敗或超出 minmax 時輸入框標紅、顯示原因並設定 aria-invalid,原本的值不會被清掉;輸入框清空後失焦則視為清除
  • 民國元年=西元 1912 年。民國模式下遇到 1911 年(含)以前的日期,年份標籤、下拉選單與顯示字串都直接用西元,不會出現「民國 −3 年」;meta.roc.year 仍會如實回傳 0 或負數方便你自行判斷
  • 年份下拉預設涵蓋民國元年到 2100 年;有 minmax 時縮限為兩者的年份區間,月份箭頭在邊界月停用
  • 「今天」標記與「今天」按鈕依使用者裝置時間、在掛載後才計算,SSR 與客戶端首屏一致;今天不在可選範圍時按鈕停用
  • 月曆彈層以彈簧動畫開合、換月時新舊月份水平交錯滑動(AnimatePresence mode="popLayout"),選中格以 scale 彈跳強調;網格固定 6 列 42 格,換月時高度不跳動
  • 提供 name 時會另外渲染一個隱藏 input 送出 ISO 字串,顯示用的輸入框不帶 name,原生表單拿到的永遠是 YYYY-MM-DD

可及性

  • 輸入框以 label 關聯,提示/錯誤訊息置於 aria-describedby 指向的 role="status"aria-live="polite")區域,解析結果會被螢幕閱讀器朗讀;解析失敗同步設定 aria-invalid
  • 觸發按鈕為 type="button",帶 aria-haspopup="dialog"aria-expandedaria-controls;彈層為 role="dialog",Esc 關閉並把焦點還給觸發鈕,點擊元件外或焦點離開時自動收合
  • 月曆採 role="grid"rowcolumnheadergridcell,選中日 aria-selected、今天 aria-current="date"、超出範圍 aria-disabled;每一格有完整的 aria-label(年月日+星期+今天/不可選)
  • 網格採漫遊式 tabindex:只有一格可被 Tab 到;方向鍵上下左右移動一天/一週、Home/End 跳到該週首尾、PageUp/PageDown 換月(加 Shift 換年)、Enter 或空白鍵選取;跨月時檢視月份自動跟著切換
  • 曆制切換為 role="group" 內的兩顆 aria-pressed 按鈕,年份下拉是原生 select,鍵盤與螢幕閱讀器行為與一般表單控制項一致
  • 輸入框按 Enter 解析前會檢查 isComposingkeyCode 229,中文輸入法組字中不會誤送出
  • 使用者系統開啟「減少動態效果」時,彈層開合、換月滑動、選中彈跳與提示文字過場一律改為即時切換,只保留狀態與顏色變化

本頁目錄