Quartz cron 運算式需要六個必要欄位 — 秒、分鐘、小時、日期、月份和星期幾 — 後面接著一個可選的第七個年份欄位,而把一個經典的 Unix 五欄位字串貼到 Quartz 中,是開發人員在產生 Quartz cron 運算式時最常犯的單一錯誤。Quartz Cron 運算式產生器從源頭消除那個錯誤,方式是將輸入限制在文件記載的欄位順序、對兩個日期欄位套用必要的問號配對、在輸出前驗證數值範圍,並產生一段簡明英文摘要供檢閱。由於此工具完全在您的瀏覽器中執行,從不建立或執行工作,剩下的風險位於下游:排程器的時區設定錯誤、未測試的日光節約時間轉換、保留為預設值的失火 (misfire) 政策,以及在所提供年份之後悄悄停止觸發的年份限制。請將產生的字串視為起點,然後在將實際執行工作的同一個 Quartz 環境中驗證接下來的多次觸發時間。

為何 Quartz cron 看起來與經典 cron 不同
產生 Quartz cron 運算式時,最可避免的最大錯誤,就是把字串當作遵循經典 Unix 五欄位配置來處理。Quartz 排程器(記載於 Quartz 2.5 CronTrigger 教學中)需要一個前導的秒欄位,因此前六個位置依序是秒、分鐘、小時、日期、月份和星期幾,第七個位置為可選的年份。在經典 cron 中可運作的「每週五中午」規則 0 12 * * 5,在 Quartz 中變成 0 0 12 ? * FRI,因為前導的 0 佔住秒的位置,而星期幾欄位必須與日期欄位中的問號配對。
那項單一的結構性差異,正是大多數 Quartz 工作從未觸發、僅觸發一次或在錯誤日期觸發的原因。當您使用 Quartz Cron 運算式產生器時,輸入控制項會直接對應到 Quartz 文件記載的欄位順序,因此輸出的字串在建構上就具備六個必要欄位,只有當您提供介於 1970 到 2099 之間的有效年份時,才會出現第七個欄位。如果您需要用於其他排程器的經典五欄位字串,標準的 Cron 運算式產生器涵蓋了該情境,並防止兩個生態系互相污染。
兩個日期欄位與問號規則
Quartz 拒絕同時評估日期與星期幾的限制,並透過為兩個欄位之一保留問號 ? 來表示該拒絕。Quartz 的 CronExpression 原始碼將 ? 視為「無特定值」,並要求其出現在未承載規則的那個日期欄位上。指定星期幾的規則必須在日期欄位放上 ?;指定月份中的編號日期或最後一天的規則必須在星期幾欄位放上 ?。兩個欄位都寫成萬用字元,或兩個都填入具體值,所產生的運算式其意義未定義,或在觸發器建構時被解析器拒絕。
產生器會為您編碼該配對。選擇「週一至週五」,它會自動將 ? 寫入日期欄位。選擇「月份最後一天」,它會將 ? 寫入星期幾欄位。您不需要記住哪個欄位要放記號,也不會不小心產生出無效的組合。
逐步使用 Quartz Cron 運算式產生器的方法
- 選擇符合您業務規則的排程形式:間隔、每小時或每天的時間、平日、單一指定星期幾,或月週期形式(編號日期、含 L 的最後一天,或含 # 的第 n 個星期幾)。
- 僅填入所選形式所顯示的欄位。建構器會隱藏不適用的欄位,這可防止最常見的錯字 — 在解析器預期為 ? 的位置放了萬用字元。
- 若排程應在特定日曆年份後停止,請輸入該年份;將年份留空代表每個支援的年份,而不是僅限當年。
- 產生運算式。閱讀六欄位順序,確認在有日期限制時,恰好有一個日期欄位是 ?,並檢閱簡明英文摘要作為健全性檢查。
- 複製該字串,並貼到目標 Quartz 觸發器中。然後在將實際執行工作的同一個 Quartz 環境中,測試接下來的多次觸發時間、排程器時區、日光節約處理,以及失火指示。
這些步驟直接對應到產生器已驗證的操作流程,並讓檢閱與執行保持分離:此工具從不部署工作,因此最終驗證必須始終在您的排程器中進行。
產生器所驗證的數值範圍與具名星期幾
Quartz 對欄位邊界相當嚴格,超出文件記載範圍的值,不是在觸發器建構時擲出剖析錯誤,就是默默地偏移排程。產生器在輸出前會拒絕超出範圍的數字。已驗證的限制為:間隔 1 到 59、分鐘 0 到 59、小時 0 到 23、編號月份日期 1 到 31、第 n 次出現 1 到 5,以及可選年份為空白或介於 1970 到 2099 之間。星期幾會以 Quartz 的名稱 SUN 至 SAT 輸出,消除了經典 cron 中 0-6 與 1-7 慣例之間的歧義。
| 欄位 | 可接受範圍 | 產生器中的行為 |
|---|---|---|
| 間隔(秒或分鐘) | 1 到 59 | 所選欄位的遞增從零開始。 |
| 分鐘 | 0 到 59 | 每小時的規則固定分鐘,並讓小時保持開放。 |
| 小時 | 0 到 23 | 每日規則同時設定小時與分鐘。 |
| 月份日期 | 1 到 31 | 31 號在較短的月份中僅是不存在而已。 |
| 第 n 個星期幾出現 | 1 到 5 | 與 # 星期幾名稱配對,例如 MON#2。 |
| 可選年份 | 空白或 1970 到 2099 | 設定值可使觸發器在該年後停止觸發。 |
由於輸出具有確定性且絕不會默默地箝制無效值,所見即為 Quartz 將剖析的內容。
Quartz 擴充功能:L 與 #,以及建構器未涵蓋的部分
兩個 Quartz 特有的字元常出現於真實的排程中。L 表示「最後」,在日期欄位中支援為 L(代表月份最後一天),在星期幾欄位中支援為 L(代表月份中指定星期幾的最後一次出現),例如 5L 代表最後一個星期五。# 將星期幾名稱與序數配對,以表示「月份中的第 n 個星期幾」,例如 MON#2 代表第二個星期一。產生器僅公開這些規則的一個聚焦子集 — 最後一天,以及第 n 個星期幾 — 因為這些涵蓋了最常見的業務需求,而不會招致含糊的組合。
建構器刻意不公開所有支援的組合。最近的工作日 W、相對於 L 的位移(例如 L-3)、任意清單、月份名稱、混合遞增、行事曆物件,以及手動編輯的專家運算式皆不可用。當您的規則需要其中任何一項時,請對照官方的 CronExpression 文件建構字串,然後在目標 Quartz 專案中測試。將產生器視為完整文法本身就是一個錯誤。
在您真實的 Quartz 排程器中測試運算式
產生器產生的字串不含時區。Quartz 觸發器與排程器所設定的時區,共同決定工作實際觸發的時間,而本地時鐘時間在日光節約變更期間可能會被跳過或重複。請確認排程器的時區,然後呼叫觸發器的 API,在將實際執行工作的同一個 Quartz 環境中檢查接下來的多次觸發時間。請特別留意失火指示 — 若排程器在某個排定時刻處於停機狀態,預設行為可能與您對復原的預期不符。
兩個實用的檢查可抓出大多數剩餘的錯誤。首先,產生一個有年份限制的運算式,並確認觸發器在邊界年份停止觸發,因為年份限制可能會悄悄地讓排程退役。其次,挑一個像 31 號這樣的日子,並確認觸發器會自然跳過沒有 31 號的月份,而不是被改寫為當月最後一天;產生器不會改寫日期,因此任何關於「最後一天」的業務規則,必須明確選取最後一天的選項。這些檢查並非可選,因為產生器不會計算未來的觸發時間,也無法自行模擬日光節約或失火行為。
產生器所能防止的常見錯誤
對照已驗證的產品合約進行檢視,建構器在設計上所能防止的錯誤,正是經典上會搞壞 Quartz 部署的那些錯誤。下表將每個陷阱與產生器的行為配對列出。
| 錯誤 | 產生器改為執行的動作 |
|---|---|
| 將五欄位的 Unix cron 字串貼到 Quartz | 一律輸出六個必要欄位,僅在提供年份時才加上。 |
| 將日期與星期幾同時留為萬用字元或值 | 在未承載規則的日期欄位中精準寫入 ?。 |
| 使用與其他 cron 方言衝突的數字星期幾 | 直接輸出 SUN 至 SAT。 |
| 輸入超出文件記載範圍的數字 | 在輸出前拒絕輸入,而不是默默地箝制。 |
| 假設年份欄位代表「當年」 | 空白代表每個支援的年份;設定值會限制並可能在之後停止觸發。 |
| 未經測試就信任該字串 | 回傳簡明英文摘要作為檢閱輔助,然後預期在真實排程器中進行驗證。 |
每一列都是 Quartz CronExpression 原始碼中記載的一種失敗模式,而每一種都被產生器逐欄位的驗證所封閉。剩下的風險面是複製之後發生的事情:時區、日光節約、失火,以及在目標排程器中對一組未來觸發時間進行測試。請在排程器層級解決這些問題,Quartz cron 字串就會依照摘要所述的方式運作。
如果您需要驗證該字串在進入 Quartz 後如何被剖析,Quartz cron 運算式的時區行為是接下來要確認的事項,因為產生器從未將時區烘焙到字串本身。對於堆疊中其他位置的經典五欄位字串,標準的 Cron 剖析器可在瀏覽器中驗證並預覽下次執行時間,這可防止兩個生態系互相污染。
若要更深入了解,請參閱如何在 Linux 中取得 Cron 工作清單。