Phân tích biểu thức Cron

Nhập biểu thức cron và xem nó có nghĩa là gì bằng ngôn ngữ dễ hiểu, kèm những lần chạy sắp tới theo múi giờ bạn chọn.

File không bao giờ rời khỏi thiết bị của bạn

Ý nghĩa lịch chạy

  • Mỗi 15 phút, từ phút 0 đến phút 59
  • Từ giờ 9 đến giờ 17
  • Mỗi ngày trong tháng
  • Mỗi tháng
  • Từ Thứ Hai đến Thứ Sáu

Năm trường, đọc từ trái sang phải

Một biểu thức cron gồm 5 trường cách nhau bằng khoảng trắng — phút, giờ, ngày-trong-tháng, tháng, thứ-trong-tuần — mỗi trường nêu những giá trị nào của đơn vị đó khiến lịch chạy khớp ở lượt này. Lấy */15 9-17 * * MON-FRI làm ví dụ: trường phút */15 giãn ra 0, 15, 30, 45; trường giờ 9-17 giãn ra mọi giờ từ 9 đến 17; ngày-trong-tháng và tháng đều để *, khớp mọi giá trị; và MON-FRI thu hẹp thứ-trong-tuần về Thứ Hai tới Thứ Sáu. Gộp lại: cứ 15 phút một lần, từ 9 giờ sáng tới 5 giờ chiều, từ Thứ Hai tới Thứ Sáu — một job giờ hành chính điển hình.

TrườngVị tríPhạm vi* nghĩa là
Phút10–59mọi phút
Giờ20–23mọi giờ
Ngày trong tháng31–31mọi ngày trong tháng
Tháng41–12 hoặc JAN–DECmọi tháng
Thứ trong tuần50–7 (0 và 7 đều là Chủ Nhật) hoặc SUN–SATmọi thứ trong tuần

Bước nhảy đếm từ điểm bắt đầu của khoảng, không phải từ 0

9-17/4 đọc là "cứ 4 giờ một lần, bắt đầu từ 9, tới hết 17" — tức 9, 13, 17. */4 một mình lại khác: không có điểm bắt đầu rõ ràng, nó nhảy từ số 0 của trường, nên trên trường giờ đó là 0, 4, 8, 12, 16, 20. Hai cách viết trông gần giống nhau nhưng cho ra hai kết quả hoàn toàn khác nhau.

Mẹo: Đọc một bước nhảy lạ mắt, hãy nhìn phần bên trái dấu gạch chéo trước: * một mình nhảy từ số 0 của trường, còn một khoảng thì nhảy từ điểm bắt đầu của chính khoảng đó. 9-17/4*/4 trên cùng một trường rơi vào những giờ hoàn toàn khác nhau.

Quy tắc hay bị hiểu ngược: hai trường ngày dùng OR, không dùng AND

Cron dùng AND giữa mọi trường với nhau — trừ ngày-trong-tháng và thứ-trong-tuần, hai trường này dùng OR mỗi khi không trường ngày nào là *. 0 0 13 * FRI chạy lúc nửa đêm vào ngày 13 của tháng hoặc vào bất kỳ Thứ Sáu nào — không phải chỉ Thứ Sáu ngày 13. Chạy thử từ ngày 1/1/2026: lịch chạy Thứ Sáu 2/1, Thứ Sáu 9/1, rồi tới ngày 13 (rơi vào Thứ Ba, khớp thuần túy nhờ ngày-trong-tháng), rồi Thứ Sáu 16/1. Chỉ khi một trong hai trường là * thì quy tắc mới thu về giống AND: 0 0 * * FRI để ngày-trong-tháng là *, nên lịch chạy đơn giản là mọi Thứ Sáu.

Múi giờ: cái gì đổi, và hai ngày trong năm gây rắc rối

Biểu thức cron mô tả một thời điểm theo đồng hồ treo tường, không phải một thời điểm cố định — 30 9 * * * nghĩa là "09:30, theo múi giờ bạn chỉ định", và chọn múi giờ khác chỉ đổi thời điểm UTC tương ứng, còn giờ đọc trên đồng hồ địa phương giữ nguyên. Đặt biểu thức đó chạy từ 1/1/2026 tại Asia/Ho_Chi_Minh, lần chạy đầu rơi vào 02:30 UTC; đặt đúng biểu thức đó tại America/New_York, lần chạy đầu lại rơi vào 14:30 UTC — cách nhau 12 tiếng, dù cả hai đều đọc là 09:30 trên đồng hồ.

Hai ngày trong năm khiến việc quy đổi này thật sự rắc rối, và chỉ ở múi giờ có áp dụng giờ mùa hè (DST). Ngày 8/3/2026, đồng hồ tại America/New_York nhảy thẳng từ 01:59 sang 03:00, nên 02:30 không tồn tại hôm đó — job hằng ngày đặt 30 2 * * * đơn giản là không chạy vào ngày 8, rồi chạy lại bình thường vào ngày 9. Ngày 1/11/2026, cùng múi giờ đó lùi lại một tiếng, nên 01:30 xảy ra hai lần; job 30 1 * * * vẫn chỉ liệt kê một lần, quy về lần sớm hơn trong hai lần đó.

Mẹo: Hãy chọn múi giờ đúng theo nơi job thật sự cần chạy — giờ địa phương của server, hoặc của người dùng. UTC không bao giờ có khoảng trống hay lần lặp, nhưng một lịch viết theo UTC sẽ lệch một tiếng so với giờ địa phương mong muốn trong phần lớn năm, ở bất kỳ múi giờ nào có áp dụng DST.

Câu hỏi thường gặp

Biểu thức cron có 5 trường, theo thứ tự nào?
Phút (0–59), giờ (0–23), ngày-trong-tháng (1–31), tháng (1–12 hoặc JAN–DEC), và thứ-trong-tuần (0–7, cả 0 lẫn 7 đều là Chủ Nhật, hoặc SUN–SAT) — luôn đúng 5 trường theo thứ tự đó. Lấy `*/15 9-17 * * MON-FRI` làm ví dụ: trường phút `*/15` giãn ra thành 0, 15, 30, 45; trường giờ `9-17` giãn ra mọi giờ từ 9 đến 17; ngày-trong-tháng và tháng để `*`, khớp mọi giá trị; còn `MON-FRI` thu hẹp thứ-trong-tuần về Thứ Hai tới Thứ Sáu. Gộp lại: cứ 15 phút một lần, từ 9 giờ sáng tới 5 giờ chiều, từ Thứ Hai tới Thứ Sáu.
Vì sao `0 0 13 * FRI` chạy vào ngày 13 HOẶC bất kỳ Thứ Sáu nào, chứ không phải Thứ Sáu ngày 13?
Cron dùng AND giữa hầu hết các trường, riêng ngày-trong-tháng và thứ-trong-tuần thì dùng OR mỗi khi không trường ngày nào là `*`. `0 0 13 * FRI` chạy lúc nửa đêm vào ngày 13 của tháng, hoặc vào bất kỳ Thứ Sáu nào — cái nào tới trước. Chạy thử từ ngày 1/1/2026: lịch chạy Thứ Sáu 2/1, Thứ Sáu 9/1, rồi tới ngày 13 (rơi vào Thứ Ba, khớp thuần túy nhờ ngày-trong-tháng), rồi Thứ Sáu 16/1. Quy tắc OR chỉ áp dụng khi không trường ngày nào là `*` — `0 0 * * FRI` để ngày-trong-tháng là `*`, nên không có gì để OR kết hợp cùng, và lịch chạy đơn giản là mọi Thứ Sáu.
Bước nhảy như `9-17/4` bắt đầu đếm từ đâu?
Từ điểm bắt đầu của chính khoảng đó, không phải từ giá trị nhỏ nhất của trường. `9-17/4` là 9, 13, 17 — cứ 4 giờ một lần, bắt đầu từ 9. Còn `*/4` một mình lại khác: không có điểm bắt đầu rõ ràng, nó nhảy từ số 0 của trường, nên trên trường giờ đó là 0, 4, 8, 12, 16, 20. Hai cách viết trông gần giống nhau nhưng cho ra hai kết quả hoàn toàn khác.
Thứ-trong-tuần `0` và `7` có phải cùng một ngày không?
Đúng, cả hai đều là Chủ Nhật — đây là cách viết thay thế có chủ đích để một tuần có thể bắt đầu từ đầu này hay đầu kia. Đó cũng là lý do khoảng `5-7` hợp lệ và giãn thành Thứ Sáu, Thứ Bảy, Chủ Nhật: nó đi qua 5, 6, 7 rồi gập số 7 cuối cùng về lại 0, thay vì bị từ chối vì vượt quá phạm vi 0–6 thông thường của trường.
Chọn múi giờ khác thì cái gì thực sự thay đổi?
Biểu thức cron mô tả một thời điểm theo đồng hồ treo tường, không phải một thời điểm cố định, nên đổi múi giờ chỉ đổi thời điểm UTC tương ứng, còn giờ đọc trên đồng hồ địa phương vẫn giữ nguyên. Đặt `30 9 * * *` chạy từ 1/1/2026 tại Asia/Ho_Chi_Minh, lần chạy đầu rơi vào 02:30 UTC; đặt đúng biểu thức đó tại America/New_York, lần chạy đầu lại rơi vào 14:30 UTC — cách nhau 12 tiếng, nhưng cả hai đều đọc là 09:30 trên đồng hồ được yêu cầu.
Một job chạy hằng ngày vào giờ không tồn tại, như 02:30 ngày đồng hồ vặn sớm, thì sao?
Nó bị bỏ qua, không bị dời sang giờ khác. Ngày 8/3/2026, đồng hồ tại America/New_York nhảy thẳng từ 01:59 sang 03:00, nên 02:30 không tồn tại hôm đó. Job hằng ngày đặt `30 2 * * *` đơn giản là không chạy vào ngày 8, rồi chạy lại bình thường vào ngày 9 — công cụ hiển thị đúng khoảng trống đó thay vì tự đoán một giờ gần đó để thay thế.
Một job có chạy hai lần vào ngày đồng hồ lùi lại không?
Không — nó chỉ được liệt kê một lần. Ngày 1/11/2026, America/New_York lùi lại một tiếng, nên 01:30 xảy ra hai lần trong ngày đó. Job hằng ngày `30 1 * * *` vẫn chỉ hiện một lần chạy ngày 1/11, được quy về lần sớm hơn trong hai lần đó — bảng xem trước lịch chạy không bao giờ hiện cùng một ngày hai lần cho một job vốn chỉ định chạy một lần một ngày.