Cron schedules fire based on a five-field expression that specifies minute, hour, day of month, month, and day of week. Each field uses specific values or wildcards to determine exactly when a task runs, with the system checking these conditions at every minute interval. Understanding how these fields combine allows you to create precise schedules without relying on guesswork.
The Basic Structure of a Cron Expression
A standard cron expression consists of five fields separated by spaces. The order is always fixed: Minute, Hour, Day of Month, Month, Day of Week. There is no separator between fields other than whitespace, and leading zeros are optional but often used for alignment. When the system checks the clock, it compares the current time against each field. If both the day-of-month and day-of-week fields are restricted (meaning neither is *), the job runs if either one matches the current date. Otherwise, all fields must match the current time for the job to execute.
The fields operate independently but must all agree for execution to occur. For example, if the minute field says 0 and the hour field says 9, the job runs only when the clock reads exactly 09:00. Adding a day constraint like Mon-Fri means it runs at 09:00 only on weekdays. This logical AND behavior is critical: the job does not run if it is 9:00 AM on a Saturday, because the day-of-week condition failed.
Understanding the Five Standard Fields
Each field accepts specific types of values: single numbers, ranges, lists, or step values. Wildcards (*) mean "any value." Ranges (1-5) include both endpoints. Lists (1,3,5) check against discrete values. Step values (*/10) mean "every 10 units."
Here is how the fields map to time units:
| Field | Range | Example Value | Meaning |
|---|---|---|---|
| Minute | 0-59 | 0 | On the hour |
| Hour | 0-23 | 9 | At 9 AM |
| Day of Month | 1-31 | * | Every day |
| Month | 1-12 | * | Every month |
| Day of Week | 0-7 (Sunday=0 or 7) | 1-5 | Monday through Friday |
A common mistake is confusing Day of Month with Day of Week. If you set Day of Month to 1 and Day of Week to 5, the job runs if it is either the 1st of the month OR Friday, depending on the implementation. Most modern cron implementations treat these as an OR condition when both are specified, though some strict POSIX implementations require both to match. Always test your specific environment if you use both non-wildcard values.
How Seconds and Nicknames Extend Cron
Standard cron does not include a seconds field. If you need sub-minute precision, you often see a six-field expression where the first field is seconds. This is common in Quartz Scheduler and some Java-based systems, but not in traditional Unix cron. In these systems, the format becomes: Second, Minute, Hour, Day of Month, Month, Day of Week.
Many systems also support shorthand nicknames to simplify common patterns. These are equivalent to specific five-field expressions:
@hourly:0 * * * *@daily:0 0 * * *@weekly:0 0 * * 0@monthly:0 0 1 * *@yearly:0 0 1 1 *@reboot: Run once when the system boots
Using nicknames is cleaner for simple schedules, but they lack the flexibility of custom expressions. For instance, @daily always runs at midnight, whereas 0 9 * * * runs at 9 AM. Choose nicknames for standard intervals and explicit expressions for custom times.
Interpreting Time Zones and Daylight Saving
Cron jobs run in the server's local time zone unless explicitly configured otherwise. This creates ambiguity during daylight saving time transitions. When clocks spring forward, a job scheduled for 2:30 AM might not run that day if the clock jumps from 1:59 AM to 3:00 AM. Conversely, when clocks fall back, a job scheduled for 1:30 AM might run twice if the hour repeats.
Most cron daemons handle this by checking the current hour and minute against the schedule. If the hour skips, the job misses its window. If the hour repeats, the job may fire twice depending on the daemon's logic. To avoid these issues, schedule critical jobs outside the typical DST transition hours (usually 1 AM to 3 AM) or ensure your server uses UTC, which does not observe daylight saving time.
When working in distributed teams, confirm whether the server uses UTC or local time. A job scheduled for 0 9 * * * runs at 9 AM server time. If the server is in New York and you are in London, the job runs at 2 PM your time during winter and 1 PM during summer. Consistency in time zone selection prevents unexpected delays or early executions.
Verifying Your Schedule with Next Fire Times
The most reliable way to understand a cron expression is to see its next execution times. You can verify logic by checking if the output matches your intent. Consider the expression 0 9 * * 1-5.
This breaks down as:
- Minute:
0(at the start of the hour) - Hour:
9(at 9 AM) - Day of Month:
*(any day) - Month:
*(every month) - Day of Week:
1-5(Monday through Friday)
The resulting schedule is "Runs at 9:00 AM every weekday." To confirm this, look at the next five firing times. If today is Monday, the next fires should be Monday 9:00 AM, Tuesday 9:00 AM, Wednesday 9:00 AM, Thursday 9:00 AM, Friday 9:00 AM, then skip Saturday and Sunday, resuming Monday. If the preview shows Saturday or Sunday, the day-of-week field is misconfigured.
For complex expressions, use a tool to visualize the output. CronExplain converts expressions like */15 9-17 * * MON-FRI into plain English ("Runs every 15 minutes between 9 AM and 5 PM on weekdays") and lists the next ten firing times. This immediate feedback loop helps catch off-by-one errors or misunderstood wildcards before deploying to production.