cronuru
Guide

How Cron Expressions Work

Cron expressions look cryptic but follow a small, consistent grammar. This guide walks through the five fields of a standard cron expression, explains the special characters, and shows the patterns you'll write most often — plus the edge cases that trip up even experienced engineers.

Updated

Anatomy of a cron expression

A cron expression is a single line of text with five space-separated fields. Each field controls one unit of time:

* * * * *
│ │ │ │ │
│ │ │ │ └── day of week (0–6, Sun=0)
│ │ │ └──── month (1–12)
│ │ └────── day of month (1–31)
│ └──────── hour (0–23)
└────────── minute (0–59)

A literal asterisk * means “every valid value for this field.” So * * * * * means “every minute of every hour of every day of every month, every day of the week” — the most frequent schedule possible in standard cron.

The five fields

FieldRangeExampleMeaning
Minute0–5930At minute 30
Hour0–2314At 2 PM
Day of month1–311On the 1st of the month
Month1–12 or JAN–DEC3 or MARIn March
Day of week0–6 or SUN–SAT1 or MONOn Monday

Day-of-week 0 is Sunday in Unix and Kubernetes. Quartz and EventBridge use 1 for Sunday and 7 for Saturday. Spring accepts both 0 and 7 as Sunday.

Special characters

Beyond literal numbers and *, four characters appear in every cron dialect:

  • Comma , — list multiple values. 0,15,30,45 in the minute field means “at :00, :15, :30, and :45.”
  • Hyphen - — range of values. 1-5 in the day-of-week field means “Monday through Friday.”
  • Slash / — step value. */15 in the minute field means “every 15th minute starting from 0” (i.e., :00, :15, :30, :45). 2/10 starting from 2 means :02, :12, :22, :32, :42, :52.
  • Asterisk * — every value.

Quartz, AWS EventBridge, and a handful of other dialects add more characters: ? (no specific value), L (last day of month/week), W (weekday nearest a given day), and # (the nth occurrence of a weekday in a month). See the Cron Dialect Comparison for the full list.

Common patterns

ExpressionMeaning
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour, on the hour
0 0 * * *Every day at midnight (00:00)
0 9 * * 1-5At 9 AM on weekdays (Mon–Fri)
0 0 1 * *At midnight on the 1st of every month
0 0 * * 0At midnight every Sunday
0 12 * * 6,0At noon on weekends (Sat + Sun)
30 14 1,15 * *At 2:30 PM on the 1st and 15th of every month

Gotchas worth knowing

The day-of-month vs day-of-week OR trap. If both fields are set to anything other than *, the job runs on days that match either field — not both. So 0 0 1 * 1 runs at midnight on the 1st of every month and every Monday. To run “on the 1st only if it’s a Monday,” you have to gate it inside your script. See the DOM/DOW trap guide for details.

Daylight saving time. Traditional Unix cron uses the system’s local timezone, which causes a daily job scheduled at 2:30 AM to skip on spring-forward day and run twice on fall-back day. Always set CRON_TZ=UTC at the top of your crontab if your workload spans timezones. Kubernetes CronJob defaults to UTC unless spec.timeZone is set.

Minimum granularity is one minute. Cron cannot fire more often than once per minute. For sub-minute scheduling, use a long-running process with an in-process scheduler (e.g. setInterval, time.Tick, asyncio.sleep), a job queue (Celery, Sidekiq), or systemd timers with OnUnitActiveSec=30s.

Overlapping runs. Most cron daemons happily spawn a new instance while the previous one is still running. If your job can take longer than the interval, guard with a lockfile (flock), set concurrencyPolicy: Forbid on Kubernetes CronJobs, or pick a longer interval.

Frequently asked questions

How many fields does a cron expression have?
Standard Unix cron uses 5 fields: minute, hour, day-of-month, month, day-of-week. Some dialects extend this — Quartz adds seconds at the front (6 fields) and an optional year at the end (7 fields). AWS EventBridge uses 6 fields with year but no seconds.
What is the smallest interval cron can express?
Standard Unix cron has a minimum granularity of one minute. Quartz, Spring, and other 6-field dialects can schedule down to one second. If you need sub-second precision, cron is the wrong tool — use a job queue or in-process scheduler.