The 0 9 * * 1-5 Expression
The cron expression 0 9 * * 1-5 fires at 9:00 AM, Monday through Friday. Breaking down the five fields: minute (0), hour (9), day of month (*), month (*), day of week (1-5). The * in day of month and month means “any day of any month,” but the day of week constraint overrides that, and the job only runs when the current weekday falls between 1 (Monday) and 5 (Friday).
Day of Week Numbering
The day of week field in standard cron uses this numbering:
| Number | Day |
|---|---|
| 0 | Sunday |
| 1 | Monday |
| 2 | Tuesday |
| 3 | Wednesday |
| 4 | Thursday |
| 5 | Friday |
| 6 | Saturday |
| 7 | Sunday (alias for 0) |
Both 0 and 7 mean Sunday. This is an intentional design choice to avoid off by one confusion. The range 1-5 covers Monday through Friday. To target just Tuesday and Thursday, you would write 2,4.
Named Day Aliases
Vixie cron, cronie, and most modern cron daemons accept three letter abbreviations:
0 9 * * MON-FRI # equivalent to 0 9 * * 1-5
0 9 * * MON,WED,FRI # Monday, Wednesday, Friday only
0 9 * * TUE,THU # Tuesday and Thursday
The named form is more readable in crontabs that others will maintain. Both forms produce the same schedule. If you are writing a crontab entry that will be deployed across multiple systems with different cron implementations (or if it will be consumed by a scheduling library), stick to numeric values to avoid compatibility issues.
An Important Edge Case: dom and dow Interaction
The cron specification has a nonobvious rule: if both the day of month field and the day of week field are set to something other than *, the job runs on the union of the two constraints, not the intersection.
For example:
0 9 15 * 1-5 # runs on the 15th of the month OR any Monday-Friday
This is not “run on weekdays only if it’s the 15th.” It means “run on the 15th of any month, plus run on every weekday.” If you want weekdays AND the 15th (intersection), you need to handle that in your script logic, not in the cron expression itself.
In 0 9 * * 1-5, the day of month is *, so this edge case does not apply. The job strictly runs on weekdays.
Business Hours Scheduling Patterns
Morning standup reminder:
0 9 * * 1-5 # fires at 9:00 AM weekdays
Deployment checks at the start and end of business hours:
0 9,17 * * 1-5 # 9 AM and 5 PM, weekdays only
Polling an API every 15 minutes during business hours:
*/15 9-17 * * 1-5 # every 15 min, 9 AM to 5 PM, weekdays
Slack notification for a nightly build, but only on weekdays:
0 8 * * 1-5 # 8 AM weekdays, report on overnight CI results before standup
Real World Uses for Weekday Only Schedules
Slack and notification bots
Sending automated Slack messages about system health, deployment status, or daily metrics only makes sense when people are online. A weekday only morning cron avoids sending notifications that no one will see over the weekend.
Deployment health checks
Some teams run automated smoke tests or canary checks at the start of each business day to catch overnight drift before engineers start debugging reports. A 9 AM weekday cron is a natural trigger for this.
Database maintenance during off hours
Jobs that run vacuum, analyze, or index operations might be scheduled for early morning weekdays to avoid weekend batch processing that uses the same database resources.
Report distribution
Daily operational reports sent to a team via email or Slack do not need to run on weekends. Restricting to weekdays saves unnecessary runs and keeps inboxes quieter.
Combining Weekday Restriction with Hour Ranges
You can restrict to business hours without writing 8 separate cron entries:
*/30 9-17 * * 1-5
This fires every 30 minutes from 9:00 AM to 5:30 PM (note: the hour range means the job fires for any execution where the hour is between 9 and 17 inclusive, so the last fire of the day is at 17:30). If you want to cut off exactly at 5:00 PM:
0,30 9-16 * * 1-5 # fires at :00 and :30 for hours 9 through 16, last run at 16:30
Or, if you want the last run at 17:00 specifically:
0,30 9-17 * * 1-5 # last runs: 17:00 and 17:30
Choose based on your exact cutoff requirement.
Checking Next Execution Times
from croniter import croniter
from datetime import datetime
cron = croniter("0 9 * * 1-5", datetime.now())
for _ in range(5):
print(cron.get_next(datetime))
Use the cron builder on this page to interactively see the next 10 run times and verify the schedule before deploying it.