About Cron Parser
A cron expression is five fields - minute, hour, day of month, month, day of week - each accepting a number, a list, a range, a step, or an asterisk. This tool parses one and tells you in plain language what it means and when it next fires, which is the only reliable way to check an expression before it goes into production.
The field that catches everyone is day-of-week versus day-of-month. When both are set to something other than *, standard cron treats them as an OR, not an AND: '0 0 1 * 1' runs on the 1st of the month AND on every Monday, not only on Mondays that fall on the 1st. If you want the intersection you cannot express it in cron - you need a day-of-week schedule plus a date check inside the job.
Step values are the other common misreading. */15 in the minute field means minutes 0, 15, 30 and 45 - it counts from the start of the range, not from the moment you deploy. And a step in the hour field like 0 */5 * * * fires at 00:00, 05:00, 10:00, 15:00 and 20:00, then again at 00:00 - only four hours after the previous run, because the pattern restarts at midnight rather than continuing every five hours.
Dialects differ in ways that matter. Standard Unix cron and most schedulers take five fields; Quartz (Java, and many enterprise schedulers) takes six or seven with a leading seconds field and treats day-of-week differently; AWS EventBridge takes six and requires ? in one of the day fields. An expression copied between them will either be rejected or, worse, silently mean something else.
Parsing happens in your browser, so nothing is transmitted.
How to use the Cron Parser
- Paste the cron expression - five fields separated by spaces.
- Read the description in plain language, and check it against what you intended.
- Look at the next few fire times, which is where an off-by-one in a step or range becomes obvious.
- If both day-of-month and day-of-week are set, remember the result is the union of the two, not the intersection.
Examples
-
Every 5 minutes
*/5 * * * *
Cron Parser in code
The same operation this tool performs, in the languages you are most likely to need it.
# ┌───────────── minute (0-59)
# │ ┌─────────── hour (0-23)
# │ │ ┌───────── day of month (1-31)
# │ │ │ ┌─────── month (1-12 or JAN-DEC)
# │ │ │ │ ┌───── day of week (0-6, Sunday=0, or SUN-SAT)
# │ │ │ │ │
# * * * * *
*/5 * * * * # every 5 minutes (:00, :05, :10 ...)
0 * * * * # every hour, on the hour
0 9 * * 1-5 # 09:00, Monday to Friday
30 2 * * 0 # 02:30 every Sunday
0 0 1 * * # midnight on the 1st of every month
0 0 * * 0 # midnight every Sunday
15 3 * * 6 # 03:15 every Saturday
0 */6 * * * # 00:00, 06:00, 12:00, 18:00
0 0 1 1 * # midnight, 1 January
# The trap - this is an OR, not an AND:
0 0 1 * 1 # the 1st of the month AND every Monday
import parser from "cron-parser";
const it = parser.parseExpression("0 9 * * 1-5", {
tz: "Europe/London", // set this explicitly - see pitfalls
});
for (let i = 0; i < 5; i++) {
console.log(it.next().toISOString());
}
// Validate before saving a user-supplied expression
function isValidCron(expr) {
try { parser.parseExpression(expr); return true; }
catch { return false; }
}
from croniter import croniter
from datetime import datetime
import zoneinfo
base = datetime.now(zoneinfo.ZoneInfo("Europe/London"))
it = croniter("0 9 * * 1-5", base)
for _ in range(5):
print(it.get_next(datetime))
# Human-readable description
from cron_descriptor import get_description
print(get_description("0 9 * * 1-5")) # "At 09:00 AM, Monday through Friday"
# Standard Unix cron / Kubernetes CronJob - 5 fields
0 9 * * 1-5
# Quartz (Java, Spring @Scheduled) - 6 or 7 fields, seconds first,
# and day-of-week is 1-7 with Sunday=1
0 0 9 ? * MON-FRI
# AWS EventBridge - 6 fields, requires "?" in one day field,
# and is always UTC
cron(0 9 ? * MON-FRI *)
# Kubernetes: set the timezone explicitly or you get UTC
# spec:
# schedule: "0 9 * * 1-5"
# timeZone: "Europe/London" # k8s 1.27+
When you need this
- Checking that a schedule you are about to deploy fires when you think it does.
- Working out why a job ran at an unexpected time, or twice.
- Translating a schedule described in words into a valid expression.
- Converting an expression between Unix cron, Quartz and EventBridge dialects.
- Validating a user-supplied cron expression before storing it.
Common problems and what causes them
- Day-of-month and day-of-week combined as OR
- When both fields are set to something other than *, cron runs on either condition, not both. '0 0 13 * 5' fires on the 13th of every month and on every Friday. To get 'Friday the 13th' you need '0 0 13 * *' plus a day-of-week check inside the job.
- Steps restarting at the top of the range
- 0 */5 * * * fires at 00:00, 05:00, 10:00, 15:00, 20:00 - and then 00:00, which is four hours later, not five. Steps count from the start of the field's range, so any step that does not divide the range evenly produces an uneven gap.
- Timezone assumptions
- System cron uses the machine's local timezone; Kubernetes CronJobs and AWS EventBridge default to UTC. A schedule that is correct on a developer's laptop can run an hour or several hours off in production. Set the timezone explicitly wherever the scheduler allows it.
- Daylight saving transitions
- In a local timezone, a job scheduled at 02:30 may not run at all on the day clocks jump forward, and may run twice when they go back. For anything where exactly-once matters, schedule in UTC or make the job idempotent.
- Sunday numbered differently between dialects
- Unix cron uses 0-6 with Sunday as 0 (and accepts 7 as Sunday too). Quartz uses 1-7 with Sunday as 1. The same numeric expression therefore means a different day depending on the scheduler.
- Assuming a minimum interval of one minute is enough
- Standard cron has no seconds field, so the finest granularity is one minute. Sub-minute scheduling needs a different mechanism - Quartz's seconds field, or a long-running process with its own timer.
- Overlapping runs
- Cron starts a job on schedule regardless of whether the previous run has finished. A five-minute schedule for a job that sometimes takes seven minutes will eventually run concurrent copies. Add a lock, or use a scheduler with a concurrency policy.
FAQ
- What is the cron expression for every 5 minutes?
- */5 * * * * - which fires at :00, :05, :10 and so on. It counts from the top of the hour, not from when you deployed it.
- How do I run a job every day at 9am on weekdays?
- 0 9 * * 1-5. The 1-5 is Monday to Friday, since Sunday is 0 in standard cron. Set the scheduler's timezone explicitly, or you may get 09:00 UTC.
- Why did my job run on the wrong day?
- Most likely the day-of-month/day-of-week OR: with both fields set, cron fires when either matches. Failing that, check the timezone - Kubernetes and EventBridge default to UTC while system cron uses local time.
- What is the difference between */5 and 0/5?
- In standard cron, */5 is the step syntax and 0/5 is not valid. Quartz accepts 0/5 to mean 'starting at 0, every 5', which is the same thing - so an expression copied from a Quartz example may be rejected by Unix cron.
- Can cron run more often than once a minute?
- Not standard five-field cron, which has no seconds field. Quartz's six-field form does. For sub-minute work, use a long-running process with an internal timer rather than trying to force cron.
- Does cron handle daylight saving time?
- Poorly, when running in a local timezone: a job in the skipped hour does not run, and one in the repeated hour runs twice. Schedule in UTC where correctness matters, and make jobs idempotent.
Related reading
- Timestamp converter check fire times