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

how do i avoid mistakes when i generate quartz cron expression when using quartz cron expression generator
產生 Quartz Cron 運算式時避免犯錯

為何 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 運算式產生器的方法

  1. 選擇符合您業務規則的排程形式:間隔、每小時或每天的時間、平日、單一指定星期幾,或月週期形式(編號日期、含 L 的最後一天,或含 # 的第 n 個星期幾)。
  2. 僅填入所選形式所顯示的欄位。建構器會隱藏不適用的欄位,這可防止最常見的錯字 — 在解析器預期為 ? 的位置放了萬用字元。
  3. 若排程應在特定日曆年份後停止,請輸入該年份;將年份留空代表每個支援的年份,而不是僅限當年。
  4. 產生運算式。閱讀六欄位順序,確認在有日期限制時,恰好有一個日期欄位是 ?,並檢閱簡明英文摘要作為健全性檢查。
  5. 複製該字串,並貼到目標 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 到 3131 號在較短的月份中僅是不存在而已。
第 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 工作清單。