Quartz Cron 運算式產生器所產生的 Quartz cron 字串完全沒有時區欄位,因此瀏覽器的本地時鐘永遠不會影響該運算式或執行它的排程器。產生器輸出的純文字排程由秒、分、時、日、月、星期,以及可選的年組成,無論頁面是在設定為 UTC、America/New_York 還是 Asia/Tokyo 的瀏覽器中開啟,該字串都完全相同。Quartz 排程器接著會依據觸發條件或排程器本身所設定的時區來解讀這個字串,而不是依據產生運算式之機器的時區。這種分離正是為何同一個運算式能在不同地區的機器上不變地執行,也正是「瀏覽器時區問題」的答案是否定的。控制工作實際何時觸發的所有因素,包括牆上時間、日光節約時間的轉換,以及被略過或重複的時段,都是在目標 Quartz 環境中決定,而非在輸入字串的網頁中決定。

does the expression use my browser time zone when using quartz cron expression generator
Quartz Cron 運算式會使用瀏覽器的時區嗎?

為何 Cron 字串沒有時區欄位

Quartz cron 運算式是純粹的排程定義。它們描述一天中的哪個時刻、月份中的哪一天、以及星期中的哪一天,但從不說明該排程位於世界何處。像 2026-03-15T10:00:00+09:00 這種時間戳會將時差編入數值中,但像 0 0 10 ? * MON-FRI 這樣的 cron 運算式只包含秒、分、時、日、月、星期,以及可選的年。

Quartz 格式將秒放在最前面,並在最後加上可選的年。這比傳統 Unix cron 多一個欄位,也比時間戳少一個考量。由於字串中沒有編碼時差,只要讓每個排程器指向各自的時區,同一個運算式就能在不同地區重複使用。

下表顯示 Quartz 與傳統 Unix cron 之間的欄位順序差異。多出的秒欄與可選的年欄,是 Quartz 格式在貼入非 Quartz 排程器前需要特別注意的結構性原因。

位置Quartz 欄位傳統 Unix cron 欄位備註
1秒無此欄位Quartz 從秒開始
2分分0-59
3時時0-23
4日日當星期欄承載規則時使用 ?
5月月1-12
6星期星期Quartz 輸出 SUN 至 SAT
7年(可選)無此欄位Quartz 允許 1970-2099

由於字串中沒有時差欄位,瀏覽器的本地時區就沒有任何可以洩漏進去的地方。瀏覽器渲染頁面,頁面輸出字串,從那一刻起,該字串就不再是瀏覽器的函數。

產生器如何建立時區中立的字串

產生器完全在瀏覽器中執行,從不聯繫伺服器。這個設計選擇保證了輸出與瀏覽器的時區無關:沒有任何伺服器端的標準化步驟能以 UTC 重新解讀輸入,也沒有任何客戶端函式庫在驗證前轉換牆上時間。

在內部,產生器將受限的排程選擇對應到 Quartz 文件中從秒到星期的欄位順序。間隔選擇會產生從零開始的秒或分鐘增量。每小時排程固定分鐘,而讓小時不受限制。每日排程設定時與分。工作日排程在星期欄輸出 MON-FRI,每週排程則輸出單一具名的工作日。

每月排程最為細微。指定日期會在星期欄放入 ?。當月最後一天排程在日欄輸出 L,在星期欄放入 ?。第 n 個工作日排程(例如第二個星期一)會在星期欄使用文件規定的 # 運算子,並在日欄放入 ?。數值輸入在任何字串輸出前會先經過驗證:間隔必須為 1 至 59,分鐘為 0 至 59,小時為 0 至 23,指定日期為 1 至 31,第 n 個出現為 1 至 5,而可選年份必須留空或在 1970 至 2099 之間。八個外部測試案例會將秒、分鐘、每小時、每日、工作日、每週五、當月最後一天,以及第二個星期一的輸出,對照 Quartz 文件與原始碼行為進行鎖定,確保字串是確定性的,絕不會悄悄夾住無效的值。

在產生器中建立 Quartz 運算式

建立流程刻意簡短。每個步驟只針對單一決策,讓輸出在被複製前可以被檢視。

  1. 從產生器中選擇 Quartz 排程形狀:間隔、每小時、每日、工作日、每週、按日每月、當月最後一天,或當月第 n 個工作日。
  2. 只輸入該規則所顯示的欄位。間隔需要 1 至 59,每日需要時與分,每月需要日或第 n 個出現加上工作日名稱,而年份是可選的,必須留空或在 1970 至 2099 之間。
  3. 閱讀六欄位的輸出與英文摘要。確認欄位順序、確認日與星期不會同時持有實際值、確認問號出現在未使用的日欄位,以及確認任何 L 或 # 都位於支援的位置。
  4. 複製運算式,與輸出時完全一致,不要加上任何尾隨空白。
  5. 將它貼到目標 Quartz 排程器的設定中,並將排程器或觸發條件的時區設定為擁有該排程的時區。
  6. 測試接下來的數個觸發時間,在將執行工作的同一個 Quartz 環境中進行,特別留意日光節約時間轉換前後的時段。

在哪裡設定 Quartz 使用的時區

cron 字串沒有時區。時區在三個層級決定,正確的層級取決於你的應用程式如何啟動 Quartz。

在觸發條件層,CronTrigger 提供了 getTimeZone() 方法,觸發條件產生器接受 withTimeZone() 呼叫。當 JVM 以 UTC 執行時,觸發條件層級的時區是表達「這份工作屬於紐約交易日」最乾淨的位置。在排程器層,工廠接受一個預設時區,該時區會套用至任何未明確設定時區的 CronTrigger。在整合層,像 Spring 的 SchedulerFactoryBean 這類框架會從設定屬性中讀取排程器的時區。

在任何情況下,該值都是一個 java.util.TimeZone 識別碼,而 CronExpression 在計算下一個有效時間時使用的也是同一個識別碼。共用同一個運算式但位於不同排程器的兩個觸發條件,會在各自時區的同一個牆上時間觸發,這正是當開發人員假設瀏覽器的時差不知何故被編入時會被打破的行為。

日光節約時間轉換與牆上時間略過

一旦設定了時區,Quartz 會依據該時區解析每個觸發時間,包括日光節約時間變更前後那些尷尬的時段。在向前調整的地區,落在被略過時段內的本地牆上時間根本不存在;Quartz 與 Java 的 TimeZone 一樣,會前進到下一個有效瞬間。在向後調整的地區,落在重複時段內的牆上時間可能觸發兩次,排程器會從兩個有效瞬間中挑選一個。

實際的後果是,在 America/New_York 中,每日 02:30 的運算式在三月那個將 02:00 跳到 03:00 的星期日不會觸發;而在十一月那個將 01:00 重複到 02:00 的星期日,每日 01:30 的運算式只會觸發一次,而不是兩次。兩個季節中的字串是相同的。時區決定了牆上時間是被略過、重複,還是正常,而只有在真實的 Quartz 環境中測試,才能在上線前抓到這個問題。

對於必須完全跳過日光節約時間的排程(這在金融與廣播界很常見),更乾淨的選擇通常是以 UTC 觸發並在工作內部轉換,而不是將 cron 字串錨定到一年會飄移一小時兩次的牆上時間。

在啟用工作前驗證觸發時間

產生器輸出字串後就停止。它不會計算未來的觸發時間,也不會模擬誤觸行為,因為這兩者都取決於 Quartz 版本、觸發條件開始時間、行事曆排除項目、誤觸指令、排程器時區與應用程式生命週期,而這些全都位於目標排程器中,而不是在網頁中。

若要在依賴某個字串前驗證它,請在將執行工作的同一個 Quartz 環境中解析它,並對已知的參考瞬間呼叫 CronExpression.getNextValidTimeAfter()。這份 cron 解析器指南會逐步說明這種在專案中針對固定時鐘進行的驗證。Quartz 2.5 CronTrigger 教學與 CronExpression 原始碼是在檢視 2 月 29 日、30 天月份中的 31 日,以及當月最後一個工作日等邊界情況時,值得持續開啟參考的兩份資料。

若排程器在觸發時間通過的當下處於離線狀態,接下來的行為由誤觸指令決定。設定 withMisfireInstructionFireAndProceed() 的觸發條件會在復原時觸發一次,然後恢復正常節奏;withMisfireInstructionDoNothing() 則會略過錯過的時段,等待下一個排定的瞬間。選擇符合業務規則的策略,接著透過停止排程器、將系統時鐘向前推進一兩個觸發時間,並觀察復原行為來進行測試。

專注型產生器的限制

產生器刻意範圍很窄。它不開放最近的工作日 W 運算子、L 的偏移、任意清單、月份名稱、多重範圍、混合增量、行事曆排除項目,或手動編輯的專家級運算式。八個測試案例涵蓋了大多數團隊真正需要的形狀,其他所有項目都委由官方的 CronExpression 文件與專案層級的單元測試處理。

年份限制會讓觸發條件在該年之後不再觸發。將年份留空代表每個支援的年份,而不僅僅是當年,這在開發人員將去年的運算式複製到新環境時,經常是困惑的來源。像 31 這樣的日期在較短的月份中自然不會出現,而產生器並不會將其改寫為當月最後一天;當這是業務規則時,請使用明確的當月最後一天選項。將英文摘要視為檢視輔助工具,接著在上線前於正式排程器中驗證運算式、時區、誤觸策略,以及數個範例觸發時間。

如果你正在權衡選項,Cron 解析器命令列與線上工具:該選哪個對此有詳細說明。