Cú Pháp Biểu Thức Cron Được Giải Thích
Biểu thức cron là một trong những chủ đề mà các nhà phát triển học một lần, quên chi tiết và sau đó tra cứu lại sau mỗi sáu tháng. Một chuỗi năm ký tự như 30 9 * * 1-5 có thể mã hóa một lịch trình ngạc nhiên cụ thể — "mỗi ngày trong tuần lúc 9:30 sáng" — nhưng cú pháp có đủ trường hợp biên để gây nhầm lẫn thực sự: trường 0-indexed vs 1-indexed, Chủ Nhật vừa là 0 vừa là 7, sự khác biệt giữa tiêu chuẩn Unix và phần mở rộng sáu trường của Quartz, và hành vi tinh tế của giá trị bước kết hợp với phạm vi.
Hướng dẫn này bao quát cú pháp cron từ các nguyên tắc cơ bản, sau đó đi qua mọi ký tự đặc biệt với các ví dụ cụ thể. Cho dù bạn đang viết một cron job cho daemon Linux, cấu hình lịch trình GitHub Actions, hay thiết lập một quy tắc AWS EventBridge, các khái niệm cơ bản tương tự áp dụng — với một vài khác biệt nền tảng quan trọng được giải thích trên đường đi.
Năm Trường Tiêu Chuẩn
Một biểu thức cron tiêu chuẩn có năm trường được phân tách bằng dấu cách, đọc từ trái sang phải: phút, giờ, ngày trong tháng, tháng và thứ trong tuần. Mỗi trường giới hạn khi nào job chạy theo chiều đó. Một job chỉ chạy khi cả năm trường khớp đồng thời.
# Trường Vị trí Phạm vi Ký tự đặc biệt
# ─────────────────────────────────────────────────
# Phút 1 0-59 * / , -
# Giờ 2 0-23 * / , -
# Ngày trong tháng 3 1-31 * / , - ? L W
# Tháng 4 1-12 hoặc JAN-DEC * / , -
# Thứ trong tuần 5 0-7 (0=7=CN) hoặc SUN-SAT * / , - ? L #
Đọc một biểu thức như 30 9 * * 1-5: phút 30, giờ 9, bất kỳ ngày nào trong tháng, bất kỳ tháng nào, thứ-trong-tuần 1 đến 5 (Thứ Hai đến Thứ Sáu). Kết quả: 9:30 sáng mỗi ngày trong tuần.
Trường 1: Phút (0–59)
Trường phút chỉ định phút nào của giờ mà job nên chạy. 0 có nghĩa là đầu giờ, 30 có nghĩa là nửa giờ sau, 59 có nghĩa là một phút trước khi giờ chuyển. Dùng * chạy job mỗi phút.
Trường 2: Giờ (0–23)
Giờ sử dụng thời gian 24 giờ. 0 là nửa đêm, 12 là giữa trưa, 23 là 11:00 tối. Không có AM/PM trong cron — một job lúc 9:00 sáng là giờ 9, và một job lúc 9:00 tối là giờ 21.
Trường 3: Ngày Trong Tháng (1–31)
Không giống các trường khác, ngày-trong-tháng bắt đầu từ 1, không phải 0. Các giá trị hợp lệ là 1 đến 31. Nếu bạn chỉ định một ngày không tồn tại trong một tháng nhất định (ví dụ, ngày 31 vào tháng Tư), job đơn giản không chạy tháng đó. Trong Quartz, ký tự đặc biệt L đại diện cho ngày cuối của tháng, bất kể tháng đó có bao nhiêu ngày.
Trường 4: Tháng (1–12)
Giá trị tháng chạy từ 1 (Tháng Một) đến 12 (Tháng Mười Hai). Nhiều scheduler cũng chấp nhận các từ viết tắt ba chữ cái: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC. Chúng không phân biệt chữ hoa chữ thường trong hầu hết các triển khai.
Trường 5: Thứ Trong Tuần (0–7)
Đây là trường có nhiều di sản lịch sử nhất. Cả 0 và 7 đều đại diện cho Chủ Nhật — đây là một đặc điểm tương thích từ Unix sớm. Thứ Hai là 1, Thứ Ba 2, đến Thứ Bảy 6. Tên ba chữ cái cũng hoạt động ở đây: SUN, MON, TUE, WED, THU, FRI, SAT.
Khi cả ngày-trong-tháng và thứ-trong-tuần đều được chỉ định (không cái nào là *), hầu hết các daemon cron Unix dùng logic OR: job chạy nếu nó khớp một trong hai trường. Điều này khiến nhiều nhà phát triển ngạc nhiên. Nếu bạn muốn "ngày 15, nhưng chỉ nếu đó là ngày làm việc," bạn cần xử lý điều đó trong chính script của job, không phải trong biểu thức cron.
Ký Tự Đặc Biệt
Dấu Hoa Thị (*) — Mọi Giá Trị
Dấu hoa thị khớp mọi giá trị hợp lệ cho một trường. Trong trường phút, * có nghĩa là phút 0 đến 59. Trong trường tháng, nó có nghĩa là cả 12 tháng. Đây là ký tự phổ biến nhất trong biểu thức cron.
Dấu Gạch Chéo (/) — Giá Trị Bước
Dấu gạch chéo định nghĩa một khoảng bước. */5 trong trường phút có nghĩa là "mỗi 5 phút." Chính xác hơn, nó có nghĩa là "mọi giá trị trong phạm vi chia hết cho 5 bắt đầu từ giá trị đầu tiên trong phạm vi." Vì vậy */5 trong phút đánh giá thành 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55.
Bước có thể được kết hợp với phạm vi: 10-30/5 trong trường phút có nghĩa là mỗi 5 phút giữa phút 10 và phút 30, mang lại 10, 15, 20, 25, 30. Bước bắt đầu từ giới hạn dưới của phạm vi.
Dấu Phẩy (,) — Danh Sách Giá Trị
Dấu phẩy tạo một danh sách các giá trị cụ thể. 1,3,5 trong trường tháng có nghĩa là Tháng Một, Tháng Ba và Tháng Năm. 9,17 trong trường giờ có nghĩa là 9:00 và 17:00. Bạn có thể liệt kê nhiều giá trị cần thiết và kết hợp chúng với các cấu trúc khác.
Dấu Gạch Ngang (-) — Phạm Vi
Một dấu gạch ngang chỉ định một phạm vi bao gồm. 1-5 trong trường thứ-trong-tuần có nghĩa là Thứ Hai đến Thứ Sáu. 9-17 trong trường giờ có nghĩa là 9 giờ sáng đến 5 giờ chiều. Phạm vi bao gồm ở cả hai đầu.
Dấu Hỏi (?) — Không Có Giá Trị Cụ Thể (Chỉ Quartz)
Cron Unix tiêu chuẩn không hỗ trợ ?. Trong Quartz Scheduler, nó được dùng trong các trường ngày-trong-tháng hoặc thứ-trong-tuần để có nghĩa là "tôi không quan tâm về trường này." Vì Quartz không thể luôn giải quyết xung đột giữa ngày-trong-tháng và thứ-trong-tuần, một trong hai phải được đặt thành ? khi cái kia được chỉ định. 0 0 15 * ? có nghĩa là "ngày 15 của mỗi tháng, bất kỳ thứ nào trong tuần." 0 0 ? * MON có nghĩa là "mỗi Thứ Hai, bất kỳ ngày nào trong tháng."
L — Cuối (Chỉ Quartz)
L trong trường ngày-trong-tháng có nghĩa là ngày cuối của tháng. L trong trường thứ-trong-tuần có nghĩa là Thứ Bảy, hoặc khi kết hợp với một số (ví dụ, 5L), lần xuất hiện cuối cùng của thứ đó trong tháng. 5L là Thứ Sáu cuối cùng của tháng.
W — Ngày Trong Tuần Gần Nhất (Chỉ Quartz)
W trong trường ngày-trong-tháng tìm ngày trong tuần gần nhất (Thứ Hai–Thứ Sáu) với ngày đã cho. 15W có nghĩa là ngày trong tuần gần nhất với ngày 15. Nếu ngày 15 là Thứ Bảy, job chạy vào ngày 14 (Thứ Sáu). Nếu nó là Chủ Nhật, nó chạy vào ngày 16 (Thứ Hai). W không vượt qua ranh giới tháng.
Dấu Thăng (#) — Thứ Thứ N Trong Tháng (Chỉ Quartz)
# chỉ định lần xuất hiện thứ N của một thứ trong một tháng. 5#2 có nghĩa là Thứ Sáu thứ hai của tháng. 2#1 có nghĩa là Thứ Ba đầu tiên. Nếu lần xuất hiện được chỉ định không tồn tại trong một tháng nhất định, job không chạy tháng đó.
Các Mẫu Cron Phổ Biến
# Mỗi phút
* * * * *
# Mỗi ngày lúc nửa đêm (00:00)
0 0 * * *
# Mỗi ngày lúc 9:30 sáng
30 9 * * *
# Mỗi Thứ Hai lúc 8:00 sáng
0 8 * * 1
# Ngày đầu của mỗi tháng lúc giữa trưa
0 12 1 * *
# Mỗi 15 phút
*/15 * * * *
# Các ngày trong tuần lúc 6:00 tối
0 18 * * 1-5 Bạn có thể kiểm tra và xác thực bất kỳ cái nào trong số này bằng công cụ Cron Parser, hiển thị năm lần chạy tiếp theo cho bất kỳ biểu thức nào và làm nổi bật cú pháp không hợp lệ. Dùng Timestamp Converter nếu bạn cần xác minh một timestamp Unix cụ thể tương ứng với gì trong múi giờ địa phương của bạn.
Phần Mở Rộng 6 Trường: Quartz Scheduler
Quartz Scheduler — được dùng nhiều trong hệ sinh thái Java và được nhiều scheduler doanh nghiệp chấp nhận — thêm một trường giây bắt buộc ở đầu biểu thức. Định dạng trở thành: giây phút giờ ngày-trong-tháng tháng thứ-trong-tuần.
# Quartz 6 trường: giây phút giờ ngày tháng thứ
# Chạy chính xác lúc 00 giây, 30 phút, mỗi giờ
0 30 * * * ?
# Chạy lúc nửa đêm mỗi ngày
0 0 0 * * ?
# Chạy mỗi 5 giây
0/5 * * * * ?
# Ngày cuối của tháng lúc 10:00 sáng
0 0 10 L * ?
# Thứ Sáu cuối cùng của tháng lúc 3:00 chiều
0 0 15 ? * 6L
Các khác biệt chính so với cron tiêu chuẩn: trường giây (0–59) là đầu tiên và bắt buộc; thứ-trong-tuần dùng 1 (Chủ Nhật) đến 7 (Thứ Bảy), không phải 0–6; ? được yêu cầu trong ngày-trong-tháng hoặc thứ-trong-tuần khi cái kia được chỉ định; và L, W, và # là các ký tự đặc biệt hợp lệ.
Không bao giờ dán biểu thức Quartz vào crontab Unix mà không loại bỏ trường giây và điều chỉnh đánh số thứ-trong-tuần. Các bộ phân tích không thể hoán đổi.
Cron Trong Các Hệ Thống Khác Nhau
crontab Linux/Unix (Vixie cron)
Hình thức ban đầu và phổ biến nhất. Năm trường, được phân tách bằng dấu cách, theo sau bởi lệnh. Các biến môi trường như CRON_TZ hoặc TZ đặt múi giờ. Crontab người dùng được chỉnh sửa bằng crontab -e; crontab hệ thống sống trong /etc/cron.d/. Các phím tắt @reboot, @daily, @hourly, @weekly và @monthly được hỗ trợ rộng rãi như các bí danh cho các mẫu phổ biến.
GitHub Actions
GitHub Actions dùng cú pháp cron 5 trường tiêu chuẩn, nhưng múi giờ luôn là UTC — không có cách nào để chỉ định múi giờ địa phương trong chính biểu thức. Các biểu thức được đánh giá ở cấp kho lưu trữ, không phải theo job. Lưu ý rằng các workflow được lên lịch có thể bị trì hoãn vài phút trong các thời kỳ tải cao trên hạ tầng GitHub.
# Cú pháp lịch trình GitHub Actions (chỉ UTC 5 trường)
on:
schedule:
# Chạy mỗi ngày lúc 02:30 UTC
- cron: '30 2 * * *'
# Chạy mỗi Thứ Hai lúc 09:00 UTC
- cron: '0 9 * * 1' Tài liệu cron GitHub Actions chính thức lưu ý rằng khoảng thời gian tối thiểu là 5 phút — các biểu thức chạy thường xuyên hơn thế bị bỏ qua âm thầm.
AWS EventBridge (CloudWatch Events)
AWS EventBridge hỗ trợ cả biểu thức tốc độ (rate(5 minutes)) và biểu thức cron. Biến thể cron của họ là 6 trường nhưng khác Quartz: thứ-trong-tuần dùng tên Sun–Sat hoặc 1–7 trong đó 1 là Chủ Nhật, tất cả các thời gian là UTC, và ít nhất một trong ngày-trong-tháng hoặc thứ-trong-tuần phải là ?. AWS không hỗ trợ ký tự W hoặc #.
# AWS EventBridge (cron trong UTC, biến thể 6 trường)
# Chạy mỗi ngày lúc 10:00 sáng UTC
cron(0 10 * * ? *)
# Chạy lúc 6:00 chiều ngày trong tuần cuối cùng của mỗi tháng
cron(0 18 L-1 * ? *) Kubernetes CronJob
Kubernetes CronJob dùng cú pháp cron 5 trường tiêu chuẩn. Múi giờ mặc định là múi giờ của tiến trình kube-controller-manager (thường là UTC trên các cluster được quản lý). Kubernetes 1.25 thêm trường timeZone vào spec CronJob, cho phép cấu hình múi giờ theo từng job mà không phụ thuộc vào múi giờ hệ thống.
Node.js (node-cron / cron npm)
Gói node-cron phổ biến hỗ trợ các biểu thức 5 trường tiêu chuẩn cộng với trường giây thứ 6 tùy chọn được thêm vào đầu. Gói cron tương thích với Quartz. Cả hai đều hỗ trợ chuỗi múi giờ (ví dụ, America/New_York) như một tùy chọn constructor. Kiểm tra tài liệu của gói cụ thể để xác nhận định dạng nào nó mong đợi.
Các Bẫy Phổ Biến
Giả định múi giờ
Cron chạy theo múi giờ máy chủ hoặc container, thường là UTC trong các môi trường đám mây. Một job dự định chạy lúc "9 giờ sáng giờ làm việc" được đặt là 0 9 * * * sẽ chạy lúc 9 giờ sáng UTC — có thể là 4 giờ sáng, 5 giờ sáng hoặc 11 giờ sáng trong múi giờ địa phương của bạn tùy offset và DST. Luôn đặt CRON_TZ, dùng trường múi giờ của scheduler hoặc chuyển thời gian mục tiêu của bạn sang UTC một cách rõ ràng. Timezone Converter có thể giúp bạn tìm giá trị UTC tương đương cho bất kỳ thời gian địa phương nào qua bất kỳ múi giờ nào.
Logic OR ngày-trong-tháng / thứ-trong-tuần
Trong cron Unix, khi cả hai trường ngày-trong-tháng và thứ-trong-tuần đều không phải *, daemon chạy job nếu một trong hai khớp. Vì vậy 0 9 15 * 1 chạy vào ngày 15 mỗi tháng VÀ mỗi Thứ Hai — không chỉ vào các Thứ Hai trùng với ngày 15. Để đạt logic AND, bạn phải triển khai kiểm tra bổ sung bên trong script job.
Nhầm lẫn giá trị bước với phạm vi
*/5 có nghĩa là "mỗi 5 phút bắt đầu từ 0" (0, 5, 10 ...). 5/5 có nghĩa là "mỗi 5 phút bắt đầu từ 5" (5, 10, 15 ...). 5-30/5 có nghĩa là "mỗi 5 phút giữa phút 5 và phút 30" (5, 10, 15, 20, 25, 30). Trộn lẫn các cái này với phạm vi có thể tạo ra kết quả không mong đợi nếu bạn không cố ý về giá trị bắt đầu.
Bẫy "mỗi giây"
Độ phân giải tối thiểu của cron trong định dạng 5 trường tiêu chuẩn là một phút. Bạn không thể lên lịch một cron job chạy mỗi giây hoặc mỗi vài giây bằng một crontab. Đối với lập lịch dưới phút, dùng một timer cấp ngôn ngữ (setInterval trong Node.js, APScheduler trong Python) hoặc một hệ thống hàng đợi được xây dựng có mục đích thay vì cron. Nếu bạn cần mỗi 30 giây, chạy hai cron job: một tại * * * * * và một ngủ 30 giây trước khi thực thi.
Thiếu đánh số tháng
Trong cron tiêu chuẩn, các tháng chạy từ 1 (Tháng Một) đến 12 (Tháng Mười Hai). Trong một số API ngày của ngôn ngữ lập trình, các tháng là 0-indexed (0 = Tháng Một, 11 = Tháng Mười Hai). Nếu bạn đang tạo biểu thức cron theo lập trình từ các đối tượng ngày, hãy kiểm tra kỹ rằng bạn không sai một đơn vị trong trường tháng.
Xác Thực Biểu Thức Của Bạn
Cron Parser trên Toova hiển thị năm lần chạy được lên lịch tiếp theo cho bất kỳ biểu thức nào, hỗ trợ cả cú pháp Quartz 5 trường và 6 trường, và đánh dấu các giá trị không hợp lệ inline. Để kiểm tra nhanh, crontab.guru là một công cụ tương tác nổi tiếng mô tả các biểu thức bằng tiếng Anh đơn giản. Cả hai đều hữu ích để giữ trong dấu trang.
Khi làm việc với dấu thời gian và các thời gian được lên lịch, Timestamp Converter dịch giữa dấu thời gian Unix và ngày dễ đọc qua bất kỳ múi giờ nào. Đối với các job liên quan đến lập lịch nhạy cảm với múi giờ, ghép nó với Timezone Converter để xác minh các offset, chuyển đổi DST và các giá trị UTC tương đương trước khi viết biểu thức cuối cùng của bạn.