Developer Time & Timestamps · How-To
How to Write a Cron Expression (With Examples)
Cron syntax looks cryptic until you know the five fields and four symbols it's built from. Here is exactly how to read any cron expression, build your own from scratch, and avoid the one mistake that silently runs a job on the wrong day.
Key takeaways
- A cron expression is five fields, in order — minute, hour, day of month, month, day of week — each one narrowing down exactly when a job runs.
- Four symbols do almost all the work:
*(any value),,(a list),-(a range), and/(a step). - Build an expression by working outward from the smallest unit — pick the minute, then the hour, then which days apply.
- If you restrict both day-of-month and day-of-week away from
*, cron runs the job when either matches, not when both do — a well-known trap. - Shorthand macros like
@dailyand@hourlycover the most common schedules, and some schedulers add a sixth field for seconds. - Cron runs in whatever timezone the host or scheduler is set to — not necessarily yours — so check before scheduling anything time-sensitive.
Open almost any server's crontab and you'll find a line like 0 9 * * 1-5 sitting above a command, with no explanation attached. It looks like noise, but it is a complete, precise sentence: "run this at minute 0 of hour 9, on any day of the month, in any month, on weekdays." Once you know the grammar, that sentence reads as fast as English.
This guide covers that grammar properly: what each of the five fields means, the handful of symbols that combine them, a repeatable method for building any schedule from a plain description, and the single gotcha — mixing day-of-month and day-of-week — that catches almost every developer at least once. If you just want a working expression right now, the cron expression generator builds one for you; if you want to actually understand the syntax, read on.
01What a cron expression actually is
A cron expression is a compact schedule, not a record of a moment in time. It tells a scheduler when to run something, repeatedly, forever — "every weekday at 9 AM," "every 15 minutes," "the first of every month." That's a different job from a Unix timestamp, which records one specific instant that already happened, counted in seconds since the Unix epoch. A cron expression never points at a single moment; it describes a pattern that a scheduler checks against the clock, minute by minute, forever.
The name comes from cron, the time-based job scheduler built into Unix and Linux systems since the 1970s, and "cron job" now refers to any recurring task scheduled this way — whether it actually runs through the Unix cron daemon or through a cloud scheduler that borrowed its syntax, like AWS EventBridge, Google Cloud Scheduler, or a CI/CD pipeline's scheduled trigger.
02The five fields, field by field
A standard cron expression is five fields, separated by single spaces, always in this order:
| Position | Field | Allowed values |
|---|---|---|
| 1st | Minute | 0–59 |
| 2nd | Hour | 0–23 |
| 3rd | Day of month | 1–31 |
| 4th | Month | 1–12 |
| 5th | Day of week | 0–6 (0 = Sunday) |
Every field is filled in — none can be left blank — and a field that isn't restricted is written as *. So 30 14 * * * means "at minute 30 of hour 14 (2:30 PM), every day of the month, every month, every day of the week," which is just a wordy way of saying "every day at 2:30 PM."
Most implementations also accept three-letter names in the month field (jan–dec) and day-of-week field (sun–sat) as an alternative to numbers, though sticking with numbers is safer across different systems since not every scheduler supports the names identically.
03The four symbols that do all the work
Within each field, four symbols cover almost every schedule you'll ever need to write:
*— asterisk. Means "every value."*in the hour field matches all 24 hours.,— comma. A list of specific values.1,15in the day-of-month field means "the 1st and the 15th."-— hyphen. An inclusive range.9-17in the hour field means "9 AM through 5 PM, every hour in between."/— slash. A step value, usually paired with*or a range.*/15in the minute field means "every 15th minute, starting from 0" — so minute 0, 15, 30, and 45.
These combine freely. 0,30 9-17 * * 1-5 means "at the top and bottom of every hour, from 9 AM through 5 PM, Monday through Friday" — a reminder every half hour during the business week.
04How to build your own expression
Rather than trying to write the whole expression at once, fill the five fields left to right, starting from the smallest unit and working outward. Here's the method applied to a real request:
Goal: run a backup script every weekday at 9:00 AM
- Minute — the backup should start exactly on the hour, so the minute is
0. - Hour — 9 AM on the 24-hour clock is
9. - Day of month — no restriction, so this stays
*. - Month — no restriction, so this stays
*. - Day of week — "weekday" means Monday through Friday, written
1-5.
Result: 0 9 * * 1-5
Read it back the same way to check your work: minute 0, hour 9, any day of the month, any month, Monday through Friday. That sentence should match the request you started with, word for word — if it doesn't, one of the five fields is wrong.
05Try it: build a cron expression
Pick a value for each field below and watch the expression assemble itself. This covers the schedules most people need; for a wider range of presets and validation, use the full tool.
Cron expression builder
Result
Reads as: minute 0, hour 9, any day of month, any month, weekdays
Need to validate a longer or existing expression?
This builder is a teaching demo covering common cases. For custom step values, named ranges, and full validation, use the cron expression generator.
06Common cron expressions, ready to copy
Most schedules people actually need fall into a short list. Here are the ones worth having memorized or bookmarked.
| Schedule | Expression |
|---|---|
| Every minute | * * * * * |
| Every 5 minutes | */5 * * * * |
| Every hour, on the hour | 0 * * * * |
| Every day at midnight | 0 0 * * * |
Every day at 9:00 AM | 0 9 * * * |
Every weekday at 9:00 AM | 0 9 * * 1-5 |
| Every Sunday at midnight | 0 0 * * 0 |
| First day of every month, midnight | 0 0 1 * * |
| Every 6 hours | 0 */6 * * * |
| Twice daily, 6 AM and 6 PM | 0 6,18 * * * |
07The day-of-month vs day-of-week trap
Every other field in cron combines with an implicit AND — hour and minute and month all have to match. Day of month and day of week are the one exception, and it catches nearly everyone at least once.
When both fields are restricted away from *, cron runs the job if either one matches — an OR, not an AND. Write 0 0 15 * 1 intending "the 15th, if it's a Monday," and cron will actually run the job on every 15th and every Monday, because either condition alone is enough.
How to avoid it
If you only care about one of the two fields, leave the other as *. Want "every Monday," regardless of the date? Use 0 0 * * 1, not 0 0 * * 1 with a day-of-month restriction added on top. If you genuinely need "15th, but only if it's a Monday," that logic has to live in the script itself, not in the cron expression.
08Shorthand macros and the seconds field
Many cron implementations, including the versions shipped with most Linux distributions, accept shorthand strings in place of the five fields for the most common schedules:
@yearlyor@annually— once a year, at midnight on January 1st.@monthly— once a month, at midnight on the 1st.@weekly— once a week, at midnight on Sunday.@dailyor@midnight— once a day, at midnight.@hourly— once an hour, at minute 0.@reboot— once, whenever the system starts up.
A sixth field for seconds
Standard cron only goes down to the minute. Some non-standard schedulers — the Quartz library used across many Java applications, and several cloud schedulers — add an optional sixth field at the front for seconds, so an expression can read 0 0 9 * * 1-5 instead of 0 9 * * 1-5. Always check which format your specific scheduler expects before assuming five fields is correct.
09What timezone does cron run in?
A cron expression has no timezone of its own — it's just numbers. The timezone it actually runs in depends entirely on where it's running. A traditional crontab on a Linux server runs in that server's local system timezone, unless a CRON_TZ variable is set at the top of the crontab to override it. Cloud schedulers vary by provider, and several default to UTC rather than any local time.
The practical rule: never assume. Before deploying a time-sensitive schedule, confirm the exact timezone your specific cron implementation or scheduler uses, especially if the server and the team relying on the schedule are in different regions.
10Common mistakes to avoid
- Assuming day-of-month and day-of-week combine with AND. As covered above, restricting both fields together produces an OR.
- Using
*/2in the day-of-month field expecting "every other day." Because months have different lengths, this resets at the start of each month and doesn't reliably alternate. - Forgetting that minutes only run
0–59and hours only run0–23— there's no minute60or hour24in a standard expression. - Deploying a schedule without checking the scheduler's timezone, then wondering why a "9 AM" job runs at a different local hour than expected.
- Copying a five-field expression into a scheduler that expects six fields with seconds, shifting every value one position to the left.
Build and validate a cron expression in seconds
Pick your schedule from plain-English options and get a checked, ready-to-paste cron expression — free, no sign-up.
11Frequently asked questions
What are the five fields in a cron expression, in order?
In order: minute (0–59), hour (0–23), day of month (1–31), month (1–12), and day of week (0–6, where 0 is Sunday). A standard cron expression is those five fields separated by spaces, in that exact order.
What does an asterisk (*) mean in cron?
An asterisk means "every value is allowed" for that field. An asterisk in the hour field, for example, matches every hour, so the job is only restricted by whatever the other fields say.
How do I write a cron expression that runs every 15 minutes?
Use a step value in the minute field: */15 * * * *. The */15 means "every 15th value starting from 0," so the job fires at minute 0, 15, 30, and 45 of every hour.
What happens if I set both the day-of-month and day-of-week fields?
Cron treats the two fields as an OR, not an AND, whenever both are restricted away from *. The job runs whenever either field matches the current date, which is often not what people expect.
What timezone does a cron job run in?
Whatever timezone the host machine or scheduler is set to, unless a specific CRON_TZ or scheduler timezone setting overrides it. Many cloud schedulers default to UTC, while a traditional crontab on a server uses that server's local system timezone.
Is 0 or 7 used for Sunday in cron?
Both. In the day-of-week field, 0 and 7 are both accepted as Sunday in most cron implementations, so 0 and 7 in that field are interchangeable.