Cron Expression Parser Guide: Read & Debug Cron Schedules
Cron Expression Parser Guide: Read & Debug Cron Schedules
Cron expressions power almost every scheduled job in software — nightly backups,
report emails, cache cleanups, CI pipelines. Yet the five-field syntax
(*/5 * * * *) is famously easy to write and hard to read back. A cron
expression parser translates that cryptic string into plain English and shows
you the exact next execution times. Try it with our
Cron Expression Parser — paste any five-field
expression and instantly see what it means and when it will fire next.
This guide explains how cron fields work, what the special characters do, how a parser computes upcoming run times, and the mistakes that silently break schedules in production.
What Is a Cron Expression?
A classic cron expression is five space-separated fields, read left to right:
| Field | Position | Allowed values | Example |
|---|---|---|---|
| Minute | 1 | 0–59 | 30 = at minute 30 |
| Hour | 2 | 0–23 | 9 = at 9 AM |
| Day of month | 3 | 1–31 | 1 = the 1st |
| Month | 4 | 1–12 | 1 = January |
| Day of week | 5 | 0–7 (0 and 7 = Sunday) | 1-5 = Mon–Fri |
So 30 9 * * 1-5 means "at 09:30 on every weekday." Some systems (Quartz,
certain cloud schedulers) add a seconds or year field, but the five-field form
above is what crontab, Kubernetes CronJobs, and GitHub Actions all understand.
A cron expression describes a recurring pattern, not a duration.
*/5does not mean "5 minutes from now" — it means "whenever the minute value is divisible by 5."
The Four Special Characters
Almost every scheduling bug traces back to one of these:
| Character | Meaning | Example |
|---|---|---|
* | Every value in the field | * * * * * = every minute |
/ | Step values | */15 in minutes = :00, :15, :30, :45 |
- | Inclusive range | 1-5 in day-of-week = Mon through Fri |
, | Value list | 0,30 in minutes = on the hour and half hour |
They also combine: 0-30/10 means "every 10 units from 0 through 30" (0, 10,
20, 30), and 1,15 * * * in the day-of-month field fires on the 1st and 15th.
Why Use a Parser Instead of Squinting?
Reading cron backwards is error-prone because the fields are positional. Is
0 12 * * 1 "noon on Mondays" or "midnight on the 1st"? (It is noon on
Mondays — position 5 is day of week.) A parser removes the guesswork in two
ways:
- Plain-English description — the expression is decomposed field by field into a sentence a reviewer can verify.
- Concrete next run times — the parser simulates the schedule forward and lists the next five timestamps, which is the fastest way to catch an off-by-one field.
Code Examples: Computing the Next Run
A minimal scheduler scans forward minute by minute and checks each candidate against the parsed field sets. In JavaScript:
// Expand one cron field ("*/5", "1-5", "0,30") into a sorted number list
function parseField(field, min, max) {
const values = new Set();
for (const part of field.split(",")) {
if (part === "*") {
for (let i = min; i <= max; i++) values.add(i);
} else if (part.includes("/")) {
const [range, step] = part.split("/");
let [start, end] = range === "*" ? [min, max]
: range.includes("-") ? range.split("-").map(Number)
: [parseInt(range, 10), max];
for (let i = start; i <= end; i += parseInt(step, 10)) values.add(i);
} else if (part.includes("-")) {
const [start, end] = part.split("-").map(Number);
for (let i = start; i <= end; i++) values.add(i);
} else {
values.add(parseInt(part, 10));
}
}
return [...values].sort((a, b) => a - b);
}
parseField("*/15", 0, 59); // [0, 15, 30, 45]
parseField("1-5", 0, 7); // [1, 2, 3, 4, 5]
The same forward-scan idea in Python, for 30 9 * * 1-5 (09:30 on weekdays):
from datetime import datetime, timedelta
def next_runs(minute, hour, weekdays, count=3):
# weekdays: cron day-of-week numbers (0=Sun ... 6=Sat)
now = datetime(2026, 7, 30, 9, 23) # fixed for a reproducible demo
current = now.replace(second=0, microsecond=0) + timedelta(minutes=1)
runs = []
while len(runs) < count:
cron_dow = (current.weekday() + 1) % 7 # Python Mon=0 -> cron Sun=0
if current.minute == minute and current.hour == hour and cron_dow in weekdays:
runs.append(current)
current += timedelta(minutes=1)
return runs
for run in next_runs(30, 9, {1, 2, 3, 4, 5}):
print(run.strftime("%a, %b %d, %Y, %I:%M %p"))
# Thu, Jul 30, 2026, 09:30 AM
# Fri, Jul 31, 2026, 09:30 AM
# Mon, Aug 03, 2026, 09:30 AM
Note the day-of-week conversion: Python's weekday() counts Monday as 0, while
cron counts Sunday as 0. Mixing the two is a classic source of
"my job runs on the wrong day" bugs.
Hands-on: Tested with the Tool
We ran these checks in the Cron Expression Parser on July 30, 2026 at 09:23 local time:
- Default
*/5 * * * *— Description: "At minute every 5 minutes". Next 5 Scheduled Times:Thu, Jul 30, 2026, 09:25:00 AM, then 09:30, 09:35, 09:40, 09:45 — always the next minute divisible by 5, never "5 minutes from now." 0 12 * * 1— Description: "At noon, on Mon". Next runs:Mon, Aug 3, 2026, 12:00:00 PM, then Aug 10, 17, 24, 31 — five consecutive Mondays, confirming position 5 is day of week.30 9 * * 1-5— Next runs: Thu Jul 30 and Fri Jul 31 at 09:30 AM, then it skips the weekend and resumes Mon Aug 3 through Wed Aug 5. The description reads a bit mechanically ("At minute at 30 of at 9, from 1 to 5"), so treat the next-run list as the source of truth for complex expressions.0 0 1 1 *(yearly) — Description: "At midnight, at 1 of the month, in Jan". Only one run is listed (Fri, Jan 1, 2027, 12:00:00 AM) because the parser scans about one year ahead.
All timestamps are rendered in your browser's local timezone, which is exactly what a local crontab would use — but see the timezone pitfall below.
Common Mistakes
- Confusing field order — minute comes first, not hour.
0 12 * * *is noon,12 0 * * *is 00:12. - Ignoring the server timezone — cron fires in the host's timezone. A "9 AM" job on a UTC server runs at 5 PM in Beijing. Convert with our Timestamp Converter before deploying.
- Day-of-month AND day-of-week both set — in standard cron,
0 0 13 * 5fires on the 13th or on Fridays (OR semantics), not only Friday the 13th. - Assuming
*/naligns to job start — steps align to field boundaries (minute 0, hour 0), not to when you installed the job. - Forgetting Sunday is both 0 and 7 — scripts that normalize with
% 7handle it; hand-written comparisons often miss7.
Related Tools
- Cron Expression Parser — describe any five-field expression and preview its next five run times.
- Timestamp Converter — convert Unix timestamps to UTC and local time when checking what a schedule means across zones.
- Date/Time Converter — convert between ISO 8601, RFC 3339, and other date formats used in job logs.
- OTP Generator — another time-driven tool: generate TOTP codes that rotate on a fixed 30-second schedule.
When to Use This Tool Instead of Code
Writing a forward-scanning scheduler is a fun exercise, but for everyday work the tool wins:
- Reviewing someone else's crontab — paste each line into the Cron Expression Parser and read the English description instead of decoding positions in your head.
- Verifying before deploy — the next-run preview catches swapped fields in seconds; a unit test for the same thing takes far longer to write.
- Debugging "why didn't it fire?" — compare the tool's predicted times with your job logs to see whether the expression or the runner is at fault.
- Learning the syntax — tweak one character and watch the description and
schedule change; it is the fastest feedback loop for mastering
/,-,,.
Reach for code only when you need cron evaluation inside your application — and then use a battle-tested library rather than a hand-rolled parser.