lintcron
field-by-field cron lint

Lint your cron expression, field by field.

This cron expression validator flags the exact field that fails and why, translates valid schedules into plain English, and previews the next five run times in UTC. Catch a bad line before it ships to crontab, Kubernetes, GitHub Actions, or EventBridge.

UNIX 5-FIELD6-FIELD + SECONDSQUARTZ 7-FIELDNEXT 5 RUNS (UTC)
Check an expression
type your expression
try these

Cron expression syntax reference

Why does 0 9 * * 1-5 run cleanly under Vixie cron but get rejected by Quartz, and why does */3 quietly become */5 on GitHub Actions? Because "cron" is really a family of dialects: POSIX five-field, six-field with seconds, seven-field Quartz, and several cloud variants, each with its own field ranges, special characters, predefined nicknames, and failure modes, all laid out below.

Cron variant overview

VariantField countParser libraryWhere it shows upCanonical example
POSIX / Vixie5Vixie cron, ISC cron, dcron/etc/crontab, user crontabs, anacron0 9 * * 1-5
With seconds6node-cron, cron-validateNode.js schedulers, some Spring jobs0 0 9 * * 1-5
Quartz 7-field7Quartz, cron-utils, cRonstrueJava/Spring schedulers, Elastic Job0 0 9 ? * MON-FRI 2026
robfig Go5 or 6robfig/cronKubernetes CronJob, Go services0 9 * * 1-5
AWS EventBridge6 (no seconds)EventBridge schedule expressionEventBridge rules, AWS Scheduler0 12 ? * MON-FRI *
GitHub Actions5YAML schedule.cron.github/workflows/*.yml0 9 * * 1-5

Field ranges by variant

Five-field (POSIX / Vixie)

PositionFieldRangeString aliasesSpecial chars
1minute0–59none* , - /
2hour0–23none* , - /
3day-of-month1–31none* , - /
4month1–12JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC* , - /
5day-of-week0–7 (0 and 7 are Sunday)SUN MON TUE WED THU FRI SAT* , - /

Six-field (with seconds prefix)

PositionFieldRangeString aliasesSpecial chars
1second0–59none* , - /
2minute0–59none* , - /
3hour0–23none* , - /
4day-of-month1–31none* , - /
5month1–12JAN–DEC* , - /
6day-of-week0–6 or 1–7 (parser-dependent)SUN–SAT* , - /

Seven-field (Quartz)

PositionFieldRangeString aliasesSpecial chars
1second0–59none* , - /
2minute0–59none* , - /
3hour0–23none* , - /
4day-of-month1–31none* ? , - / L W
5month1–12JAN–DEC* , - /
6day-of-week1–7 (1 = Sunday)SUN–SAT* ? , - / L #
7year1970–2099none* , - /

Day-of-week numbering by dialect

Dialect01234567
Vixie / ISC / robfigSunMonTueWedThuFriSatSun
Quartzn/aSunMonTueWedThuFriSat
node-cron (6-field)SunMonTueWedThuFriSatn/a
GitHub ActionsSunMonTueWedThuFriSatSun
AWS EventBridgen/aSunMonTueWedThuFriSat

Step intervals and ranges

A step interval is written <value>/<step> where <value> is *, a single number, or a range, and <step> is the increment.

PatternReads as
*/5every 5 starting at 0
0/5every 5 starting at 0 (Quartz form)
7/30every 30 starting at 7
10-50/5every 5 from 10 to 50 inclusive
1-31/2every other day-of-month

Lists combine with steps: 0,30 9-17/2 * * 1-5 reads as “minute 0 and 30, every two hours from 9 to 17, weekdays.” Step values larger than the field maximum collapse to a single fire per cycle. GitHub Actions caps the minute step at 5; */3 * * * * in a workflow file silently rounds up to */5.

Special character cheat sheet

CharMeaningExampleReads asSupported in
*any value* * * * *every minuteall variants
,value list0 9,17 * * *9:00 AM and 5:00 PMall variants
-inclusive range0 9 * * 1-5weekdays at 9:00 AMall variants
/step interval*/15 * * * *every 15 minutesall variants
?no specific value0 0 9 ? * MON-FRIweekdays at 9:00 AMQuartz, EventBridge
Llast0 0 0 L * ?midnight on the last day of the monthQuartz, EventBridge
Wnearest weekday0 0 0 15W * ?nearest weekday to the 15thQuartz, EventBridge
LWlast weekday0 0 0 LW * ?last weekday of the monthQuartz
#nth weekday0 0 9 ? * 6#3third Friday at 9:00 AMQuartz, EventBridge
nL (DOW)last weekday-of-month0 0 12 ? * 6Llast Friday at noonQuartz

Predefined nicknames

NicknameExpands toVixie / ISCrobfig (K8s)GitHub ActionsQuartzEventBridge
@yearly0 0 1 1 *yesyesnonono
@annually0 0 1 1 *yesyesnonono
@monthly0 0 1 * *yesyesnonono
@weekly0 0 * * 0yesyesnonono
@daily0 0 * * *yesyesnonono
@midnight0 0 * * *yesyesnonono
@hourly0 * * * *yesyesnonono
@rebootrun once at startupyesnononono
@every <duration>every Go durationnoyesnonono

@every 1h30m is a robfig extension only, accepted by Kubernetes CronJob since cron v3.

Common patterns ready to copy

ExpressionVariantWhat it fires
*/5 * * * *5-fieldevery 5 minutes
0 * * * *5-fieldtop of every hour
0 9 * * 1-55-fieldweekdays at 9:00 AM
0 0 1 * *5-fieldmidnight on the 1st of each month
0 0 * * 05-fieldevery Sunday at midnight
0 0 9 ? * MON-FRI6-field Quartzweekdays at 9:00 AM, Quartz dialect
0 0 12 L * ?6-field Quartznoon on the last day of each month
0 0 0 ? * 6#36-field Quartzmidnight on the third Friday
0 15 10 ? * MON-FRI 20267-field Quartzweekdays at 10:15 AM in 2026 only
cron(0 12 * * ? *)EventBridgenoon every day (note the cron() wrapper)
rate(5 minutes)EventBridgeevery 5 minutes (rate, not cron)

Common failure patterns

SymptomCauseFix
"Expression contains 5 fields, expected 6"Pasting Unix cron into a Quartz-only systemPrefix with seconds: 0 0 9 * * MON-FRI
"Day field set twice" on EventBridge or QuartzBoth day-of-month and day-of-week are restrictedReplace the unused field with ?
Job fires more often than expected on weekdaysBoth DOM and DOW restricted in Vixie cron (logical OR)Use only one field; leave the other *
*/3 * * * * runs every five minutes on GitHub ActionsActions silently caps the minute step at 5Use */5 or accept the five-minute floor
Kubernetes rejects 0 0 9 ? * MON-FRIrobfig parser does not accept ?Replace ? with * and drop the seconds field
"Year 2200 out of range"Quartz year field caps at 2099Stop scheduling that far out, or omit the year
Expression valid but never firesDay-of-month references a date the month does not have (e.g., 0 0 31 2 *)Use L if Quartz available; otherwise split per-month entries
AWS rejects 0 12 * * MON-FRI *EventBridge requires ? in DOM or DOWcron(0 12 ? * MON-FRI *)
@daily rejected by QuartzQuartz does not support named macrosExpand to 0 0 0 * * ?
Wrong fire time on GitHub ActionsActions runs in UTC, not the repo's local timezoneConvert local time to UTC before writing the cron
Day-of-week off by one between systemsQuartz uses 1 = Sunday, Vixie uses 0 = SundayUse string aliases (MON, TUE, ...) to avoid the offset

Related concepts

Read more on /learn