Cron Expression Parser — What a Crontab Line Means and When It Next Fires
Paste a crontab line and it splits the five or six fields, lists every value each field really matches, explains the day-or-week rule, and computes the next upcoming run times from this machine's clock. No server is contacted and nothing you type leaves this device.
A · Field by field
| Field | As written | Values matched | What it means |
|---|
B · The next times it fires
| # | Date | Weekday | Time |
|---|
Field order, and the day-of-month OR day-of-week rule
The five fields, in the order cron reads them
minute hour day-of-month month day-of-week
0-59 0-23 1-31 1-12 0-7 (0 and 7 are both Sunday)
Each column accepts a single number, a list (1,15,30), a range (1-5), a range or whole field stepped (*/10, 0-59/5), and — in the month and day-of-week columns — the three-letter names JAN…DEC and SUN…SAT. Everything else in a crontab line is either an alias (@daily, @weekly, @hourly, @reboot) or a mistake.
A 6-field expression has a seconds column in front, and a 7-field one adds a year at the end. Neither exists in Vixie cron or a Linux /etc/crontab; they are the Quartz / Spring @Scheduled / EventBridge dialect. All three shapes are read, and the answer names the one that was assumed.
The rule that bites: day-of-month OR day-of-week
The two day columns do not combine the way people expect. When both are restricted, cron fires when either matches, so 0 0 13 * 5 is “the 13th of every month, and also every Friday” — not “the 13th when it falls on a Friday”. When one of them is * (or ?), only the other decides, which is how you write “the Friday the 13th”: impossible in one plain crontab line, and the usual workaround is a guard inside the job.
? means “no particular value” and is legal only in those two columns; it is the Quartz way of writing the same intent as *, and both are treated alike while ? is kept in the explanation because a scheduler that expects 6 fields will reject * in a day column and vice versa.
What “the next 8 times” is computed against
The upcoming table is built by walking the calendar forward field by field — month, then day, then hour, then minute, then second — using the browser’s own Date, so:
- Your machine’s timezone decides. A server cron uses the zone in its own config (
CRON_TZ=on Linux, the system clock elsewhere). The line below the table prints which zone was used. - Daylight-saving gaps are skipped, not invented. On the day the clock jumps from 01:59 to 03:00, a
30 2 * * *job has no matching instant, so the next day that does match is what gets reported. Whether your real server skips it, runs it once late, or runs it twice on the autumn hour is implementation-specific —man 5 crontabis the authority there, not a web page. - Months that are too short just do not match.
0 9 30 * *skips February entirely, and0 0 29 2 *only fires in a leap year, so its “next run” is usually years away. - The count is approximate by design. “Runs in a year” counts matches in the next 366 days and stops at 2000, printing
2000+after that; a per-minute line obviously blows past the cap.
Vendor extensions, refused rather than guessed
L (last day of month), W (nearest weekday), # (nth weekday, 5#3), ranges that wrap (FRI-MON), and step values of 0 all exist in some schedulers and in none that agree on the meaning. Rather than invent a reading, the tool names the token it could not parse and which field it came from — which is also what your crontab should do, because cron usually refuses the whole line silently and leaves a bad entry in the log.
Questions
Q: Why does it say “every day” for 0 0 * * 0-6?
Because 0–6 is every weekday and the day-of-month column is *; the two are the same statement. man 5 crontab treats a fully-listed day-of-week as restricted, so 0 0 13 * 0-6 would fire on the 13th and every day of the week — effectively daily.
Q: Is */5 in the hour field every five hours?
Yes — from 00, so 00:00, 05:00, 10:00, 15:00, 20:00. It is not five hours after the job last ran: cron has no memory of the previous run, it only tests the current clock against the fields.
Q: It says the next run is in 3 hours, but my server ran it already.
The most common cause is the zone: your server’s clock, not yours. Second most common is a */? mix-up in the day columns. Then the minute/hour fields being right and the command failing, which no parser can see.
Q: Does the expression get sent anywhere? No. Parsing is string splitting in the browser-side script and the times come from the browser’s own calendar — no server ever holds your expression, and what you type never leaves this device.