ไวยากรณ์ Cron Expression อธิบาย
Cron expression เป็นหนึ่งในหัวข้อที่นักพัฒนาเรียนครั้งเดียว ลืมรายละเอียด แล้วค้นใหม่ทุกหกเดือน สตริงห้าตัวอักษรเช่น 30 9 * * 1-5 สามารถเข้ารหัสตารางที่เฉพาะอย่างน่าประหลาด — "ทุกวันธรรมดาเวลา 9:30 น." — แต่ไวยากรณ์มีกรณีพิเศษพอที่จะทำให้สับสนได้จริง: ฟิลด์ที่เริ่มจาก 0 vs 1, วันอาทิตย์เป็นทั้ง 0 และ 7, ความแตกต่างระหว่างมาตรฐาน Unix และส่วนขยาย 6 ฟิลด์ของ Quartz และพฤติกรรมที่ละเอียดของค่า step รวมกับช่วง
คู่มือนี้ครอบคลุมไวยากรณ์ cron จากหลักการพื้นฐาน จากนั้นไปทีละตัวอักษรพิเศษพร้อมตัวอย่างจริง ไม่ว่าคุณจะเขียน cron job สำหรับ Linux daemon, ตั้ง schedule GitHub Actions หรือตั้งกฎ AWS EventBridge หลักการพื้นฐานเดียวกันใช้ได้ — พร้อมความแตกต่างระหว่างแพลตฟอร์มที่สำคัญไม่กี่ข้ออธิบายระหว่างทาง
ห้าฟิลด์มาตรฐาน
Cron expression มาตรฐานมีห้าฟิลด์คั่นด้วยช่องว่าง อ่านจากซ้ายไปขวา: นาที, ชั่วโมง, วันในเดือน, เดือน และวันในสัปดาห์ แต่ละฟิลด์จำกัดเมื่อ job รันตามมิตินั้น Job รันเฉพาะเมื่อทั้งห้าฟิลด์ตรงกันพร้อมกัน
# ฟิลด์ ตำแหน่ง ช่วง ตัวอักษรพิเศษ
# ─────────────────────────────────────────────────
# นาที 1 0-59 * / , -
# ชั่วโมง 2 0-23 * / , -
# วันในเดือน 3 1-31 * / , - ? L W
# เดือน 4 1-12 หรือ JAN-DEC * / , -
# วันในสัปดาห์ 5 0-7 (0=7=อาทิตย์) หรือ SUN-SAT * / , - ? L #
การอ่านนิพจน์อย่าง 30 9 * * 1-5: นาที 30, ชั่วโมง 9, วันใดของเดือน, เดือนใด, วันในสัปดาห์ 1 ถึง 5 (จันทร์ถึงศุกร์) ผลลัพธ์: 9:30 น. ทุกวันธรรมดา
ฟิลด์ 1: นาที (0-59)
ฟิลด์นาทีระบุนาที(หลายตัว)ของชั่วโมงที่ job ควรรัน 0 หมายถึงต้นชั่วโมง, 30 หมายถึงครึ่ง, 59 หมายถึงหนึ่งนาทีก่อนชั่วโมงเปลี่ยน การใช้ * รัน job ทุกนาที
ฟิลด์ 2: ชั่วโมง (0-23)
ชั่วโมงใช้เวลา 24 ชั่วโมง 0 คือเที่ยงคืน, 12 คือเที่ยงวัน, 23 คือ 11:00 ทุ่ม ไม่มี AM/PM ใน cron — job ที่ 9:00 น. คือชั่วโมง 9 และ job ที่ 9:00 ทุ่มคือชั่วโมง 21
ฟิลด์ 3: วันในเดือน (1-31)
ต่างจากฟิลด์อื่น วันในเดือนเริ่มที่ 1 ไม่ใช่ 0 ค่าที่ถูกต้องคือ 1 ถึง 31 หากระบุวันที่ไม่มีในเดือนที่กำหนด (เช่น วัน 31 ในเมษายน) job เพียงไม่รันเดือนนั้น ใน Quartz ตัวอักษรพิเศษ L แทนวันสุดท้ายของเดือน ไม่ว่าจะมีกี่วันในนั้น
ฟิลด์ 4: เดือน (1-12)
ค่าเดือนวิ่งจาก 1 (มกราคม) ถึง 12 (ธันวาคม) Scheduler หลายตัวรับอักษรย่อสามตัวด้วย: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC ในการนำไปใช้ส่วนใหญ่ไม่สนใจตัวพิมพ์
ฟิลด์ 5: วันในสัปดาห์ (0-7)
นี่เป็นฟิลด์ที่มีภาระทางประวัติศาสตร์มากที่สุด ทั้ง 0 และ 7 แทนวันอาทิตย์ — นี่คือลักษณะเข้ากันจาก Unix ยุคแรก วันจันทร์คือ 1, วันอังคาร 2, ถึงวันเสาร์ 6 ชื่อสามตัวอักษรใช้ได้ที่นี่ด้วย: SUN, MON, TUE, WED, THU, FRI, SAT
เมื่อระบุทั้งวันในเดือนและวันในสัปดาห์ (ทั้งคู่ไม่ใช่ *), cron daemon Unix ส่วนใหญ่ใช้ตรรกะ OR: job รันหากตรงกับฟิลด์ ใดฟิลด์หนึ่ง นี่ทำให้นักพัฒนาหลายคนประหลาดใจ หากต้องการ "วันที่ 15 แต่เฉพาะวันธรรมดา" คุณต้องจัดการในสคริปต์ job ไม่ใช่ใน cron expression
ตัวอักษรพิเศษ
ดอกจัน (*) — ทุกค่า
ดอกจันตรงกับทุกค่าที่ถูกต้องสำหรับฟิลด์ ในฟิลด์นาที * หมายถึงนาที 0 ถึง 59 ในฟิลด์เดือน หมายถึงทั้ง 12 เดือน เป็นตัวอักษรที่พบบ่อยที่สุดใน cron expression
Slash (/) — ค่า Step
Slash นิยามช่วง step */5 ในฟิลด์นาทีหมายถึง "ทุก 5 นาที" แม่นยำกว่านั้น หมายถึง "ทุกค่าในช่วงที่หารด้วย 5 ลงตัวเริ่มจากค่าแรกในช่วง" ดังนั้น */5 ในนาทีประเมินเป็น 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55
Step สามารถรวมกับช่วงได้: 10-30/5 ในฟิลด์นาทีหมายถึงทุก 5 นาทีระหว่างนาที 10 และนาที 30 ให้ 10, 15, 20, 25, 30 Step เริ่มจากขอบล่างของช่วง
Comma (,) — รายการค่า
Comma สร้างรายการค่าเฉพาะ 1,3,5 ในฟิลด์เดือนหมายถึงมกราคม, มีนาคม และพฤษภาคม 9,17 ในฟิลด์ชั่วโมงหมายถึง 9:00 และ 17:00 คุณสามารถแสดงรายการค่าได้มากเท่าที่ต้องการและรวมกับโครงสร้างอื่น
Dash (-) — ช่วง
Dash ระบุช่วงรวม 1-5 ในฟิลด์วันในสัปดาห์หมายถึงจันทร์ถึงศุกร์ 9-17 ในฟิลด์ชั่วโมงหมายถึง 9 โมงเช้าถึง 5 โมงเย็น ช่วงรวมทั้งสองปลาย
Question Mark (?) — ไม่มีค่าเฉพาะ (Quartz เท่านั้น)
Unix cron มาตรฐานไม่รองรับ ? ใน Quartz Scheduler ใช้ในฟิลด์วันในเดือนหรือวันในสัปดาห์เพื่อหมายถึง "ฉันไม่สนใจฟิลด์นี้" เพราะ Quartz ไม่สามารถแก้ความขัดแย้งระหว่างวันในเดือนและวันในสัปดาห์เสมอ หนึ่งในสองต้องตั้งเป็น ? เมื่อระบุอีกตัว 0 0 15 * ? หมายถึง "วันที่ 15 ของทุกเดือน, วันใดในสัปดาห์" 0 0 ? * MON หมายถึง "ทุกวันจันทร์, วันใดในเดือน"
L — Last (Quartz เท่านั้น)
L ในฟิลด์วันในเดือนหมายถึงวันสุดท้ายของเดือน L ในฟิลด์วันในสัปดาห์หมายถึงวันเสาร์ หรือเมื่อรวมกับเลข (เช่น 5L) คือการปรากฏครั้งสุดท้ายของวันในสัปดาห์นั้นในเดือน 5L คือวันศุกร์สุดท้ายของเดือน
W — วันธรรมดาที่ใกล้ที่สุด (Quartz เท่านั้น)
W ในฟิลด์วันในเดือนหาวันธรรมดา (จันทร์-ศุกร์) ที่ใกล้ที่สุดกับวันที่กำหนด 15W หมายถึงวันธรรมดาที่ใกล้กับวันที่ 15 ที่สุด หากวันที่ 15 เป็นเสาร์ job รันวันที่ 14 (ศุกร์) หากเป็นอาทิตย์ รันวันที่ 16 (จันทร์) W ไม่ข้ามขอบเขตเดือน
Hash (#) — วันในสัปดาห์ครั้งที่ N ของเดือน (Quartz เท่านั้น)
# ระบุการปรากฏครั้งที่ N ของวันในสัปดาห์ในเดือน 5#2 หมายถึงวันศุกร์ที่สองของเดือน 2#1 หมายถึงวันอังคารแรก หากการปรากฏที่ระบุไม่มีในเดือนที่กำหนด job ไม่รันเดือนนั้น
รูปแบบ Cron ทั่วไป
# ทุกนาที
* * * * *
# ทุกวันเวลาเที่ยงคืน (00:00)
0 0 * * *
# ทุกวันเวลา 9:30 น.
30 9 * * *
# ทุกวันจันทร์เวลา 8:00 น.
0 8 * * 1
# วันแรกของทุกเดือนเวลาเที่ยงวัน
0 12 1 * *
# ทุก 15 นาที
*/15 * * * *
# วันธรรมดาเวลา 6:00 เย็น
0 18 * * 1-5 คุณสามารถทดสอบและตรวจสอบสิ่งเหล่านี้ใดๆ ได้โดยใช้เครื่องมือ Cron Parser ซึ่งแสดงห้าเวลาทำงานถัดไปสำหรับ expression ใดๆ และไฮไลต์ไวยากรณ์ที่ไม่ถูกต้อง ใช้ Timestamp Converter หากต้องตรวจสอบว่า Unix timestamp เฉพาะตรงกับอะไรใน timezone ท้องถิ่นของคุณ
ส่วนขยาย 6 ฟิลด์: Quartz Scheduler
Quartz Scheduler — ใช้หนักในระบบนิเวศ Java และยอมรับโดย scheduler ระดับองค์กรหลายตัว — เพิ่มฟิลด์วินาทีบังคับที่จุดเริ่มต้นของ expression Format กลายเป็น: วินาที นาที ชั่วโมง วันในเดือน เดือน วันในสัปดาห์
# Quartz 6 ฟิลด์: วินาที นาที ชั่วโมง วัน เดือน วันในสัปดาห์
# รันที่ 00 วินาทีเป๊ะ, 30 นาที, ทุกชั่วโมง
0 30 * * * ?
# รันเที่ยงคืนทุกวัน
0 0 0 * * ?
# รันทุก 5 วินาที
0/5 * * * * ?
# วันสุดท้ายของเดือนเวลา 10:00 น.
0 0 10 L * ?
# วันศุกร์สุดท้ายของเดือนเวลา 3:00 บ่าย
0 0 15 ? * 6L
ความแตกต่างหลักจาก cron มาตรฐาน: ฟิลด์วินาที (0-59) เป็นตัวแรกและจำเป็น; วันในสัปดาห์ใช้ 1 (อาทิตย์) ถึง 7 (เสาร์) ไม่ใช่ 0-6; ? จำเป็นในวันในเดือนหรือวันในสัปดาห์เมื่อระบุอีกตัว; และ L, W และ # เป็นตัวอักษรพิเศษที่ถูกต้อง
อย่า paste expression Quartz เข้า crontab Unix โดยไม่เอาฟิลด์วินาทีออกและปรับการนับวันในสัปดาห์ Parser ไม่สามารถสลับกันได้
Cron ในระบบต่างๆ
Linux/Unix crontab (Vixie cron)
รูปแบบดั้งเดิมและพบบ่อยที่สุด ห้าฟิลด์คั่นด้วยช่องว่าง ตามด้วยคำสั่ง ตัวแปรสภาพแวดล้อมเช่น CRON_TZ หรือ TZ ตั้ง timezone Crontab ของผู้ใช้แก้ไขด้วย crontab -e; crontab ระบบอยู่ใน /etc/cron.d/ ชอร์ตคัต @reboot, @daily, @hourly, @weekly และ @monthly รองรับกว้างเป็น alias สำหรับรูปแบบทั่วไป
GitHub Actions
GitHub Actions ใช้ไวยากรณ์ cron 5 ฟิลด์มาตรฐาน แต่ timezone เป็น UTC เสมอ — ไม่มีวิธีระบุ timezone ท้องถิ่นใน expression เอง Expression ถูกประเมินที่ระดับ repository ไม่ใช่ต่อ job หมายเหตุ workflow ที่ตั้งเวลาอาจล่าช้าหลายนาทีในช่วงโหลดสูงบนโครงสร้างพื้นฐาน GitHub
# ไวยากรณ์ตาราง GitHub Actions (5 ฟิลด์ UTC เท่านั้น)
on:
schedule:
# รันทุกวันเวลา 02:30 UTC
- cron: '30 2 * * *'
# รันทุกวันจันทร์เวลา 09:00 UTC
- cron: '0 9 * * 1' เอกสาร GitHub Actions cron อย่างเป็นทางการระบุว่าช่วงเวลาขั้นต่ำคือ 5 นาที — expression ที่รันบ่อยกว่านั้นถูกข้ามแบบเงียบๆ
AWS EventBridge (CloudWatch Events)
AWS EventBridge รองรับทั้ง rate expression (rate(5 minutes)) และ cron expression แบบ cron ของพวกเขาเป็น 6 ฟิลด์แต่ต่างจาก Quartz: วันในสัปดาห์ใช้ชื่อ Sun-Sat หรือ 1-7 ที่ 1 คืออาทิตย์, เวลาทั้งหมดเป็น UTC และอย่างน้อยหนึ่งในวันในเดือนหรือวันในสัปดาห์ต้องเป็น ? AWS ไม่รองรับตัวอักษร W หรือ #
# AWS EventBridge (cron ใน UTC, แบบ 6 ฟิลด์)
# รันทุกวันเวลา 10:00 น. UTC
cron(0 10 * * ? *)
# รันเวลา 6:00 เย็นวันธรรมดาสุดท้ายของทุกเดือน
cron(0 18 L-1 * ? *) Kubernetes CronJob
Kubernetes CronJob ใช้ไวยากรณ์ cron 5 ฟิลด์มาตรฐาน Timezone ค่าเริ่มต้นเป็น timezone ของ process kube-controller-manager (มัก UTC บน cluster ที่จัดการ) Kubernetes 1.25 เพิ่มฟิลด์ timeZone ใน CronJob spec อนุญาตการตั้งค่า timezone ต่อ job โดยไม่ต้องพึ่ง timezone ระบบ
Node.js (node-cron / cron npm)
Package node-cron ยอดนิยมรองรับ expression 5 ฟิลด์มาตรฐานพร้อมฟิลด์วินาทีที่ 6 ตามตัวเลือกเติมหน้า Package cron เข้ากับ Quartz ทั้งคู่รองรับสตริง timezone (เช่น America/New_York) เป็นตัวเลือก constructor ตรวจสอบเอกสารของ package เฉพาะคุณเพื่อยืนยัน format ที่คาดหวัง
กับดักทั่วไป
การสมมติเรื่อง timezone
Cron รันใน timezone เซิร์ฟเวอร์หรือ container ซึ่งบ่อยครั้งเป็น UTC ในสภาพแวดล้อม cloud Job ที่ตั้งใจรันที่ "9 โมงเวลาทำธุรกิจ" ตั้งเป็น 0 9 * * * จะรันที่ 9 โมง UTC — ซึ่งอาจเป็น 4 โมงเช้า, 5 โมงเช้า หรือ 11 โมงเช้าใน timezone ท้องถิ่นขึ้นอยู่กับ offset และ DST ตั้ง CRON_TZ, ใช้ฟิลด์ timezone ของ scheduler หรือแปลงเวลาเป้าหมายเป็น UTC อย่างชัดเจนเสมอ Timezone Converter ช่วยหาค่าเทียบเท่า UTC สำหรับเวลาท้องถิ่นใดใน timezone ใด
ตรรกะ OR ของวันในเดือน / วันในสัปดาห์
ใน Unix cron เมื่อทั้งฟิลด์วันในเดือนและวันในสัปดาห์ไม่ใช่ *, daemon รัน job หาก ฟิลด์ใดฟิลด์หนึ่ง ตรง ดังนั้น 0 9 15 * 1 รันที่วันที่ 15 ของทุกเดือนและทุกวันจันทร์ — ไม่ใช่เฉพาะวันจันทร์ที่บังเอิญเป็นวันที่ 15 เพื่อให้ได้ตรรกะ AND คุณต้องนำการเช็คเพิ่มเติมไปใช้ในสคริปต์ job
สับสนค่า step กับช่วง
*/5 หมายถึง "ทุก 5 นาทีเริ่มจาก 0" (0, 5, 10 ...) 5/5 หมายถึง "ทุก 5 นาทีเริ่มจาก 5" (5, 10, 15 ...) 5-30/5 หมายถึง "ทุก 5 นาทีระหว่างนาที 5 และนาที 30" (5, 10, 15, 20, 25, 30) การผสมกับช่วงสามารถผลิตผลที่ไม่คาดคิดหากคุณไม่ตั้งใจเกี่ยวกับค่าเริ่มต้น
กับดัก "ทุกวินาที"
ความละเอียดต่ำสุดของ cron ในรูปแบบ 5 ฟิลด์มาตรฐานคือหนึ่งนาที คุณไม่สามารถตั้ง cron job ให้รันทุกวินาทีหรือทุกไม่กี่วินาทีโดยใช้ crontab สำหรับการตั้งเวลาต่ำกว่านาที ใช้ timer ระดับภาษา (setInterval ใน Node.js, APScheduler ใน Python) หรือระบบ queue สร้างเฉพาะแทน cron หากต้องการทุก 30 วินาที รัน cron job สอง: หนึ่งที่ * * * * * และหนึ่งที่หลับ 30 วินาทีก่อนรัน
การนับเดือนผิด
ใน cron มาตรฐาน เดือนวิ่งจาก 1 (มกราคม) ถึง 12 (ธันวาคม) ใน API วันที่ของบางภาษาเขียนโปรแกรม เดือนเริ่มจาก 0 (0 = มกราคม, 11 = ธันวาคม) หากคุณสร้าง cron expression เชิง programmatic จาก date object ตรวจสอบสองครั้งว่าไม่ผิดหนึ่งในฟิลด์เดือน
ตรวจสอบ Expression ของคุณ
Cron Parser บน Toova แสดงห้าเวลาทำงานถัดไปสำหรับ expression ใดๆ, รองรับทั้งไวยากรณ์ 5 ฟิลด์และ 6 ฟิลด์ Quartz และติดธงค่าที่ไม่ถูกต้องแบบ inline สำหรับการเช็คความสมเหตุสมผลด่วน, crontab.guru เป็นเครื่องมือโต้ตอบที่รู้จักดีที่อธิบาย expression เป็นภาษาธรรมดา ทั้งคู่มีประโยชน์ที่จะ bookmark ไว้
เมื่อทำงานกับ timestamp และเวลาที่ตั้งไว้, Timestamp Converter แปลระหว่าง Unix timestamp และวันที่อ่านได้ใน timezone ใด สำหรับ job ที่เกี่ยวกับการตั้งเวลาที่ไวต่อ timezone, จับคู่กับ Timezone Converter เพื่อตรวจสอบ offset, การเปลี่ยน DST และค่าเทียบเท่า UTC ก่อนเขียน expression สุดท้าย