lintcron

See exactly which cron field is wrong — and why.

Type a cron expression, get instant field-by-field feedback. See what it means in plain English and when it runs next. Everything happens in your browser.

*/5 * * * 8
min
hour
day
month
dow

dow: value 8 is out of range (0-7)

Cron expression syntax: where the spec ends and the runtimes diverge

POSIX.2-1992 §3 defines the canonical five-field cron syntax (minute, hour, day-of-month, month, day-of-week), and that document is the only thing every "cron" implementation actually agrees on. Everything past that line, the seconds field, the year field, the L/W/#/? characters, the timezone behavior, is an extension that some specific runtime grafted on later.

That divergence is why an expression that runs cleanly on a Linux box gets rejected by GitHub Actions, accepted but interpreted differently by Quartz, and silently truncated by AWS EventBridge.

Five-field syntax: POSIX origins and field ranges

The POSIX five-field form is unambiguous. Field 1 is minute (0–59). Field 2 is hour (0–23, where 24 is invalid and 0 means midnight). Field 3 is day-of-month (1–31). Field 4 is month (1–12 or JANDEC). Field 5 is day-of-week (0–7, where both 0 and 7 mean Sunday for historical Vixie cron compatibility). The special characters in pure POSIX are *, ,, -, and /.

# 9:00 AM every weekday, POSIX five-field
0 9 * * 1-5

Step intervals like */5 are not in POSIX.2 itself; they came in as a Vixie cron extension, then BSD and GNU adopted them, which is why every modern Unix daemon supports */5 even though the original spec does not. We treat steps as part of the de-facto POSIX baseline because rejecting them would make the validator useless against real-world crontabs.

When day-of-month and day-of-week are both restricted (neither one is *), Vixie cron applies an OR: the job fires when either field matches. ISC cron and dcron behave the same way. Quartz reverses this and requires one of the two fields to be ?. The failure mode we kept hitting in our test corpus was 0 0 15 * 1, written by someone expecting "the 15th, only if it's a Monday." On a Linux host that fires every 15th and every Monday.

The Quartz extensions: seconds, year, and L/W/#

Quartz, written for the Java scheduler ecosystem, standardized a six- and seven-field form: seconds (0–59), minute, hour, day-of-month, month, day-of-week, year (1970–2099). It also introduced four characters POSIX does not recognize.

# Quartz seven-field: 9:00:00 AM, weekdays, in 2026 only
0 0 9 ? * MON-FRI 2026

L means "last." L in day-of-month is the last day of the month; 5L in day-of-week is the last Friday. W picks the weekday nearest a date: 15W fires on the closest weekday to the 15th. # selects the nth weekday: 6#3 is the third Friday. ? is "no specific value" and exists because Quartz forbids both day-of-month and day-of-week from being constrained at once.

Where platform runtimes diverge from the spec

Special characterUnix 5-fieldQuartz 7-fieldKubernetes CronJobGitHub ActionsAWS EventBridge
*yesyesyesyesyes (? required in DOM or DOW)
, - /yesyesyesyesyes
L (last)noyesnonoyes
W (weekday nearest)noyesnonoyes
# (nth weekday)noyesnonoyes
? (no value)noyes (required)nonoyes (required)
Seconds fieldnoyesnonono
Year fieldnoyesnonoyes (6 fields, no seconds)

Kubernetes CronJob runs cron from the robfig Go library and rejects ? outright, so an expression copied from Quartz config fails the admission webhook with a parser error that doesn't name the offending character. GitHub Actions runs in UTC always and ignores any host or workflow timezone setting, which catches teams whose crontab uses local time. The Actions runner also silently caps minute steps at five, so */3 * * * * in a workflow file does not fire every three minutes; the smallest interval the scheduler respects is five. AWS EventBridge looks Quartz-shaped at first glance but drops the seconds field, leaving a six-field form that demands ? in exactly one of day-of-month or day-of-week. That's why 0 12 * * MON-FRI * is invalid on EventBridge yet valid on a Linux box. Vixie cron and ISC cron honor @reboot, @daily, @hourly, and the other named shorthands; Quartz, Kubernetes, GitHub Actions, and EventBridge reject them.

The validator picks a dialect from the field count and from optional L/W/#/? presence, then walks each field against that dialect's range, returning the field index and the reason together when something fails.

Frequently Asked Questions

How do I validate a cron expression?

Paste the expression into a cron validator like Lintcron. The tool splits it into its fields (minute, hour, day-of-month, month, day-of-week, plus optional seconds and year), checks each field against the allowed values and special characters for that position, and reports which field is invalid and why.

Why is my cron expression not working?

Common causes are an out-of-range value (hour 24 instead of 0-23), a character the dialect does not support (using L or # in standard Unix cron), the wrong field count for the platform (sending a 6-field expression to a scheduler that only accepts 5), or a timezone mismatch between your local time and the scheduler. Lintcron flags the specific field and reason so you can pinpoint which of these applies.

How do I know which field of my cron expression is wrong?

A field-aware validator like Lintcron labels each position (min, hour, day, month, dow) and flags only the field that failed, with a specific message such as "hour: value 25 exceeds maximum of 23" or "day-of-week: value 8 is out of range (0-7)." That pinpoints the error rather than rejecting the whole expression.

What is the difference between Unix cron and Quartz cron?

Unix cron uses 5 fields (minute, hour, day-of-month, month, day-of-week). Quartz cron uses 6 or 7 fields by adding a seconds field at the front and an optional year field at the end, and supports the special characters L, W, #, and ?. Day-of-week numbering also differs: Unix uses 0-7 (0 and 7 = Sunday), while Quartz uses 1-7 (1 = Sunday). Lintcron auto-detects the format from the field count and validates against the matching rule set.

How do I write a cron expression for Kubernetes CronJobs?

Kubernetes CronJob uses standard 5-field Unix cron syntax: minute, hour, day-of-month, month, day-of-week. It does not support seconds, Quartz characters (L, W, #, ?), or a year field. Older Kubernetes versions interpret schedules in the kube-controller-manager's local timezone, while newer versions support an explicit spec.timeZone field for IANA names like "America/New_York."

How do I write a cron expression for GitHub Actions?

GitHub Actions scheduled workflows use standard POSIX 5-field cron syntax: minute, hour, day-of-month, month, day-of-week. There is no seconds field and no year field, schedules always run in UTC, and there is roughly a 5-minute minimum resolution. Expressions that worked on Quartz or AWS EventBridge may silently fail or run at unexpected times because of these constraints.

What does the seconds field do in a 6-field cron expression?

In a 6-field cron expression the first field is seconds (0-59) and the remaining five are the standard minute, hour, day-of-month, month, and day-of-week fields. It lets you schedule jobs at sub-minute resolution, for example "30 0 9 * * *" runs every day at 9:00:30 AM. Standard Unix cron and most cloud schedulers (GitHub Actions, Kubernetes CronJob) do not support a seconds field — Quartz, Spring scheduler, and some custom job runners do.

Can I use L, W, or # in a Unix cron expression?

No. The characters L (last), W (nearest weekday), and # (nth weekday of the month) are Quartz cron extensions and are not valid in standard Unix cron. Tools like vixie-cron, anacron, Kubernetes CronJob, and GitHub Actions reject expressions containing them. If you need "last day of the month" or "third Friday," you either need a Quartz-compatible scheduler or a workaround such as running daily and gating execution inside the job script.

What does '0 9 * * 1-5' mean?

'0 9 * * 1-5' means "at 9:00 AM every weekday, Monday through Friday." The fields read as: minute = 0, hour = 9, day-of-month = any, month = any, day-of-week = 1-5 (Monday through Friday). Lintcron renders the same translation automatically and lists the next several run times so you can confirm the schedule matches your intent.

Why does crontab.guru not show me errors?

crontab.guru is a schedule descriptor, not a linter — it shows what a valid expression means but does not break down why an invalid one fails. Lintcron was built specifically to fill that gap: it validates each field individually and surfaces the exact error (out-of-range value, unsupported character, wrong field count) instead of just saying "invalid."

Your data stays put

No accounts. No server calls. Everything runs in your browser. See our Privacy Policy for details.