Cron Expression Parser
What it does
The Cron Expression Parser takes a five-field cron expression and translates it into a sentence anyone can understand — “At 09:00, Monday through Friday” rather than 0 9 * * 1-5. A field-by-field breakdown shows each component (minute, hour, day-of-month, month, day-of-week) with its valid range, and quick-preset buttons cover the schedules most projects need (every minute, every hour, daily at midnight or noon, every Monday at 9, first of the month, weekdays at 8). Optional toggles handle the verbose-output and Unix-style day-of-week (0–6) variants.
Common situations
You’re code-reviewing a crontab change and the new line is */15 9-17 * * 1-5. Before approving, you want to confirm “every 15 minutes during business hours on weekdays” is actually what was intended. Pasting the expression here returns the answer in seconds.
You’re documenting a scheduled job for the team’s runbook. The cron expression is correct but unreadable. The runbook entry needs both — the technical expression for ops, the plain-English version for context.
You’re auditing an inherited system and finding cron jobs scattered across multiple servers, each on a different schedule, none documented. Running each through this tool produces a written translation of the schedule for free, which is typically half the audit work.
You’re choosing a schedule for a new background job. The job runs nightly, but should it be midnight UTC or 1am local? Should it skip weekends? Working through candidate schedules in plain English (instead of debugging cron syntax) makes the decision faster.
You’re investigating why a job fired more often than expected. The cron expression looks right, but the actual fire pattern reveals an overlap you missed. Pasting the expression and reading the parsed fields surfaces the misunderstanding.
What you need to know
The parser uses cronstrue, a well-tested library that handles every syntactic quirk of cron — ranges (1-5), lists (1,3,5), steps (*/5), and the named-day shorthand (MON-FRI, JAN). The output prose is generated from the parsed structure, not pattern-matched on the input, so even unusual but valid expressions translate correctly.
A cron expression has five fields, in order: minute (0–59), hour (0–23), day-of-month (1–31), month (1–12 or JAN–DEC), day-of-week (0–6 or SUN–SAT, with 0 and 7 both meaning Sunday in some implementations). Some systems add a sixth field for seconds (0–59 at the start) or a seventh for years; standard Unix cron uses five.
The day-of-week toggle switches between two conventions. Unix cron numbers Sunday as 0; some systems (notably IBM and Quartz) start at 1. The same expression means different things depending on which convention applies, and getting it wrong is one of the most common cron bugs. Surfacing it as a toggle makes the assumption explicit.
Day-of-month and day-of-week have a non-obvious interaction: when both are restricted, most cron implementations treat them as OR (fire if either matches), not AND. 0 0 1 * 1 does not mean “midnight on Monday the 1st” — it means “midnight every 1st of the month, AND midnight every Monday”. Worth knowing if a schedule fires more than expected.
Step values (*/5) align to zero, not the field’s start. */5 in the minute field fires at :00, :05, :10… not at the time you save the cron entry plus five-minute increments. To skew, use a list (2,7,12,17,...).
Ranges are inclusive of both ends. 1-5 in day-of-week is Monday through Friday (five days), not Tuesday through Friday.
The shortcut form (@daily, @hourly, @reboot, @yearly) is supported by most cron implementations but produces non-standard expressions some parsers reject. Spell out the equivalent five-field form for portability.
Time zones matter. Most cron implementations use the system clock’s timezone. Daylight-saving transitions can cause jobs to skip or repeat — a 2:30am job runs twice on the spring-back transition, and not at all on the spring-forward. UTC-based cron avoids this; check whether your scheduler’s docs specify the timezone.
Frequently asked questions
What is a cron expression?
A 5-field text expression describing when a job should run: minute, hour, day-of-month, month, day-of-week. Each field can be a single value, a range, a list, or a wildcard (*).
How do I read 0 9 * * 1-5?
Minute 0, hour 9, any day-of-month, any month, days 1-5 of the week (Monday through Friday). Translation: 9:00 AM every weekday.
What does */15 mean?
Every 15 units of the field. In the minute field, */15 * * * * fires at :00, :15, :30, :45 of every hour. The pattern aligns to zero, not to whenever you saved the cron.
Can cron run more than once a minute?
Standard 5-field cron has minute granularity — once per minute is the maximum. For higher frequency, you need a different scheduler (systemd timers, Quartz, or a queue-based approach).
What’s the difference between Unix cron and Quartz?
Unix cron: 5 fields, day-of-week 0=Sunday. Quartz: 6 or 7 fields (adds seconds and optional year), day-of-week 1=Sunday. Same syntax for ranges and steps, but the field count and numbering differ. Specify which one your scheduler uses.
Why didn’t my cron job fire on the last day of the month?
Day-of-month doesn’t account for variable month lengths. 0 0 31 * * fires only in months with 31 days. For “last day of month”, you need either a wildcard plus an in-job date check, or a scheduler that supports L (last) like Quartz.
How do I pick a schedule that doesn’t conflict with other jobs?
Stagger by offsetting the minute field. If three jobs all run “every hour”, set them to 5 * * * *, 10 * * * *, 15 * * * * rather than all at 0 * * * *. Reduces resource contention at the top of the hour.
What are @daily, @hourly, etc.?
Cron shortcuts. @daily = 0 0 * * *. @hourly = 0 * * * *. @reboot runs once at startup. Convenient but non-standard — some cron parsers reject them.
Common problems
Problem: Job runs more often than the schedule suggests.
Day-of-month and day-of-week interact as OR, not AND. 0 0 1 * 1 fires every 1st AND every Monday — combining gives more triggers than the AND interpretation would. Use only one of the two day fields if you want strict AND.
Problem:
*/30in minute field doesn’t fire every 30 minutes.
It does — at :00 and :30 of every hour. If you expected “every 30 minutes from when I saved this”, that’s not how step values work. Steps align to zero, not to save time.
Problem: Cron job not firing on daylight-saving transition.
Spring forward skips 2:00–3:00am locally; jobs scheduled in that window don’t fire on the transition day. Spring back duplicates 1:00–2:00am; jobs in that window fire twice. Use UTC-based scheduling to avoid both, or accept the once-a-year quirk.
Problem: Day-of-week numbering produces unexpected results.
The 0 vs 1 Sunday convention. Test your scheduler with * * * * 0 — if it fires Sunday, you’re in 0-Sunday mode (Unix cron). If it fires Monday, you’re in 1-Sunday mode (Quartz). Adjust expressions accordingly.
Problem: Cron expression is correct but the job never runs.
Check the scheduler’s logs (cron’s syslog, Kubernetes CronJob events). Common causes: the binary is not executable, the path is wrong, the user doesn’t have permission, the environment lacks expected variables. Cron has a notoriously minimal environment by default.
Quick guides
Standard Unix cron: crontab -e to edit your user’s crontab. crontab -l to list. Add a line: 0 9 * * 1-5 /path/to/script runs at 9am weekdays. Cron’s environment is minimal; explicitly set PATH and any other env vars at the top.
Kubernetes CronJob: Define a CronJob resource with spec.schedule set to the cron expression. Kubernetes’ implementation supports standard 5-field cron. Logs go to the CronJob’s pods, which are auto-cleaned per successfulJobsHistoryLimit.
GitHub Actions / GitLab CI: Both support cron-syntax scheduled workflows. Use UTC explicitly (no DST confusion). Schedules with */5 granularity may be delayed during peak load — non-binding for time-sensitive jobs.
Tips
- The shortcut form
@daily,@hourly,@rebootis supported by most cron implementations but produces non-standard expressions some parsers reject. Spell out the equivalent five-field form for portability. - Test schedules with a presets button first — they expand to the canonical form, which you can then tweak.
- Step values (
*/5) align to zero, not the field’s start.*/5in the minute field fires at :00, :05, :10… not at the time you save the cron entry plus five-minute increments. To skew, use a list (2,7,12,17,...). - Ranges are inclusive of both ends.
1-5in day-of-week is Monday through Friday (five days), not Tuesday through Friday. - Stagger heavy jobs across the hour. Three “hourly” jobs at
0 * * * *all fire at the same time and contend for resources; offset them to5 * * * *,10 * * * *,15 * * * *. - Specify timezone explicitly where the scheduler supports it. UTC-based scheduling avoids DST surprises entirely.
- For “last day of month” or “last weekday” semantics, you need an extended cron (Quartz with
Lsyntax) or an in-job date check.
Related tools in this suite
The natural pairing is the Unix Timestamp Converter — cron schedules and Unix timestamps are the two languages of automated scheduling, and projects often need to translate between “this cron expression” and “this specific moment”. The JSON Formatter frequently sits next to this tool when cron entries arrive embedded in configuration files.
Take it further
Schedule complexity at scale is a system problem — cron jobs accumulating, dependencies between them going undocumented, drift between staging and production. The Beacon Bits product handles the visibility side: standalone scheduled work that would otherwise be invisible becomes monitorable, with alerts when something stops running as expected.