Cron expression for every weekday
The cron expression 0 9 * * 1-5 runs a job at 9 AM Monday through Friday — five runs per week, none on weekends. The 1-5 is the day-of-week range using Unix numbering (Sunday=0, Saturday=6). AWS EventBridge needs cron(0 9 ? * MON-FRI *) because its day-of-week numbering starts at 1 (Sun=1, Sat=7).
Quick reference
| Platform | Expression | Day-of-week numbering |
|---|---|---|
| Unix / Linux crontab | 0 9 * * 1-5 | Sun=0, Mon=1, …, Sat=6 (7 also = Sun) |
| Kubernetes CronJob | 0 9 * * 1-5 | Same as Unix |
| GitHub Actions | 0 9 * * 1-5 | Same as Unix |
| AWS EventBridge | cron(0 9 ? * MON-FRI *) | Sun=1, Mon=2, …, Sat=7 |
| Quartz (Java) | 0 0 9 ? * MON-FRI * | Same as AWS |
Why does day-of-week numbering differ between Unix and AWS?
This is one of the most stubborn cron gotchas. Unix cron (and Vixie cron, used by Linux/macOS/Kubernetes) uses 0-based numbering where Sunday is 0. AWS EventBridge and Quartz use 1-based numbering where Sunday is 1.
So:
| Day | Unix cron | AWS EventBridge |
|---|---|---|
| Sunday | 0 (or 7) | 1 |
| Monday | 1 | 2 |
| Friday | 5 | 6 |
| Saturday | 6 | 7 |
This means 0 9 * * 1-5 (weekdays 9 AM in Unix) is cron(0 9 ? * 2-6 *) in AWS — both numbers shift by one. Get it wrong and Unix’s “Mon-Fri” becomes AWS’s “Sun-Thu”, silently dropping Friday and adding Sunday.
The clearest fix: use named days everywhere AWS accepts them. cron(0 9 ? * MON-FRI *) is unambiguous — there’s no off-by-one.
Variations
| Schedule | Expression |
|---|---|
| 9 AM weekdays (the canonical example) | 0 9 * * 1-5 |
| 8:30 AM weekdays | 30 8 * * 1-5 |
| 5 PM weekdays (end-of-day report) | 0 17 * * 1-5 |
| Weekends only (Saturday + Sunday) | 0 9 * * 6,0 |
| Every weekday at 9 AM and 5 PM (twice daily) | 0 9,17 * * 1-5 |
| Weekdays in AWS (named form) | cron(0 9 ? * MON-FRI *) |
| Weekdays in AWS (numeric form) | cron(0 9 ? * 2-6 *) |
How do I use it on each platform?
Linux crontab:
0 9 * * 1-5 /usr/local/bin/morning-report
Kubernetes CronJob:
apiVersion: batch/v1
kind: CronJob
metadata:
name: morning-report
spec:
schedule: "0 9 * * 1-5"
timeZone: "America/New_York" # so 9 AM is Eastern, not UTC
jobTemplate:
spec:
template:
spec:
containers:
- name: report
image: my-org/morning-report:1.0
restartPolicy: OnFailure
AWS EventBridge Scheduler (with timezone):
Type: AWS::Scheduler::Schedule
Properties:
Name: morning-report
ScheduleExpression: 'cron(0 9 ? * MON-FRI *)' # named days are unambiguous
ScheduleExpressionTimezone: 'America/New_York'
FlexibleTimeWindow:
Mode: 'OFF'
Target:
Arn: !GetAtt ReportFunction.Arn
RoleArn: !GetAtt SchedulerRole.Arn
GitHub Actions (UTC):
on:
schedule:
- cron: '0 14 * * 1-5' # 9 AM EST = 14:00 UTC during standard time
GitHub Actions has no timezone option. If you want “9 AM Eastern”, convert to UTC and accept that DST will shift the firing time twice a year.
Common mistakes
Off-by-one when porting between Unix and AWS. * * * * 1-5 (Unix Mon-Fri) becomes cron(* * ? * 2-6 *) in AWS — both numbers shift up by one. Easier to remember: just use named days in AWS.
Using 0 for Sunday in AWS. AWS treats 0 as invalid (its range is 1-7). The Unix-style 0 0 * * 0 (Sunday in Unix) becomes cron(0 0 ? * 1 *) in AWS. Or just write SUN.
Forgetting ? in AWS when restricting day-of-week. AWS requires ? for whichever of dom/dow you’re not constraining. cron(0 9 * * MON-FRI *) is rejected; cron(0 9 ? * MON-FRI *) is the valid form.
For other schedules see common cron schedules, or build a custom expression with the Cron Expression Builder.
Frequently asked questions
- Why does Sunday have two valid numbers in Unix cron?
- Vixie cron accepts both `0` and `7` as Sunday for backwards compatibility with older System V cron implementations. So `0 0 * * 0` and `0 0 * * 7` both mean "every Sunday at midnight". Modern documentation prefers `0`. AWS EventBridge does NOT accept `7` for Sunday — it uses 1-based numbering where Sunday is `1` and Saturday is `7`.
- How do I write "every weekday" in AWS EventBridge?
- Use named days to dodge the off-by-one risk: `cron(0 9 ? * MON-FRI *)`. The numeric form is `cron(0 9 ? * 2-6 *)` because AWS counts Monday as `2` (Sunday=1). The named form is harder to get wrong and shows the intent clearly in code review.
- How do I exclude public holidays from a weekday cron?
- Cron has no concept of holidays. Either filter inside the job (check the date and exit early), use a workflow scheduler that supports calendars (Airflow, Argo Workflows), or maintain a list of explicit "skip dates" in your job logic. For a small set of US federal holidays, a 30-line wrapper script is usually enough.
Need a different schedule?
Build cron expressions for Unix, Kubernetes and AWS — with a human-readable description and the next 5 run times.
Open the Cron Expression Builder →Related
Cron expression for every 5 minutes
The cron expression `*/5 * * * *` runs every 5 minutes in Linux crontab, Kubernetes, GitHub Actions; `cron(*/5 * * * ? *)` in AWS EventBridge.
Cron expression for every 15 minutes
The cron expression `*/15 * * * *` runs every 15 minutes — at :00, :15, :30, :45 — across Linux, Kubernetes, GitHub Actions and AWS.
Cron expression for every hour
The cron expression `0 * * * *` runs once per hour on the hour. Same syntax in Linux, Kubernetes and GitHub Actions; `cron(0 * * * ? *)` in AWS EventBridge.
Cron expression for daily at midnight
The cron expression `0 0 * * *` runs once daily at midnight. Watch the timezone — Kubernetes < 1.25 and AWS EventBridge default to UTC, not local time.
Cron expression for the first of every month
The cron expression `0 0 1 * *` runs at midnight on the 1st of every month. AWS uses `cron(0 0 1 * ? *)`.
Cron expression for every Sunday
The cron expression `0 0 * * 0` runs every Sunday at midnight in Unix cron. AWS uses `cron(0 0 ? * 1 *)` because Sunday is `1` in AWS, not `0`.