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 欄位備註
1Quartz 從秒開始
20-59
30-23
4當星期欄位承載規則時使用 ?
51-12
6星期星期Quartz 輸出 SUN 至 SAT
7年(選填)Quartz 允許 1970-2099

因為字串中沒有偏移量欄位,所以瀏覽器的本地時區沒有任何東西可以洩漏進去。瀏覽器呈現頁面、頁面輸出字串,從那一刻起,該字串就不再是瀏覽器的函數。

產生器如何建構與時區無關的字串

產生器完全在瀏覽器中執行,從不聯繫伺服器。這種設計選擇正是保證輸出不受瀏覽器時區影響的原因:沒有任何伺服器端的標準化步驟可能會將輸入以 UTC 重新解讀,也沒有任何客戶端的程式庫在驗證之前轉換牆面時間。

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

每月排程是最細微的。指定日期的每月排程會在星期欄位放置 ?。每月最後一天的排程會在日欄位輸出 L,並在星期欄位放置 ?。第幾個星期幾的排程(例如第二個星期一)會在星期欄位輸出文件所記錄的 # 運算子,並在日欄位放置 ?。在任何字串輸出之前會先驗證數值輸入:間隔必須是 1 到 59,分鐘是 0 到 59,小時是 0 到 23,指定月份日期是 1 到 31,第幾次發生是 1 到 5,而選填的年必須是空白或在 1970 至 2099 之間。八個外部測試案例將秒、分鐘、每小時、每日、週日、每週五、最後一天,以及第二個星期一的輸出,與 Quartz 文件及原始碼行為進行鎖定,因此該字串是確定性的,永遠不會在無聲中箝制無效的值。

在產生器中產生 Quartz 運算式

建構流程刻意保持簡短。每一個步驟都針對單一決定,讓輸出在被複製之前可以被檢視。

  1. 從產生器中選擇 Quartz 排程的形狀:間隔、每小時、每日、週日、每週、依日期的每月、每月最後一天,或每月第幾個星期幾。
  2. 只輸入該規則所顯示的欄位。間隔需要 1 到 59,每日需要一個小時和分鐘,每月需要一個日期或一個第幾次發生以及星期名稱,年份為選填且必須為空白或在 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 字串錨定到一個一年中會漂移一小時的牆面時間。

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

產生器輸出字串後便停止。它不會計算未來的觸發時間,也不會模擬錯失觸發(misfire)的行為,因為這兩者都取決於 Quartz 版本、觸發器的開始時間、行事曆排除、錯失觸發指令、排程器時區,以及應用程式的生命週期——所有這些都存在於目標排程器中,而非網頁中。

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

如果排程器在某個觸發時間通過的當下處於離線狀態,錯失觸發指令會決定接下來會發生什麼。具有 withMisfireInstructionFireAndProceed() 的觸發器會在恢復時觸發一次,然後恢復正常節奏;withMisfireInstructionDoNothing() 會跳過錯失的時段,並等待下一個排定的瞬間。請選擇符合商業規則的策略,然後透過停止排程器、將系統時鐘向前推進一或兩個觸發時間,並觀察恢復行為來加以測試。

這個專注型產生器的限制

這個產生器刻意保持精簡。它並未公開最近的工作日 W 運算子、相對於 L 的偏移、任意清單、月份名稱、多重範圍、混合增量、行事曆排除,或由人工編輯的專家級運算式。八個測試案例涵蓋了大多數團隊實際需要的形狀,其他所有功能則委由官方的 CronExpression 文件以及專案層級的單元測試來處理。

年份限制會使觸發器在該年之後停止觸發。將年份留空意味著所有受支援的年份,而不僅僅是當前年——這是當開發者將去年的運算式複製到新環境時常見的混淆來源。像 31 號這樣的日期在較短的月份中自然沒有對應的發生,而產生器不會將其重寫為該月的最後一天;當這是商業規則時,請使用明確的「最後一天」選項。請將白話摘要視為檢視輔助工具,然後在上線前於正式排程器中驗證運算式、時區、錯失觸發策略,以及數個抽樣的觸發時間。