Cron Atlas

Cron expression for the first weekday of the month

0 0 9 1W * ?
second0
minute0
hour9
day of monthweekday near 1st
monthevery month
day of weekno value

At 09:00, on the weekday nearest the 1st of the month.

Next 10 runs

In Quartz, worked out in your browser from the current time.

At a glance

Runs
12 in 2027
Gap between runs
28 days to 33 days
Expressible in
EventBridge and Quartz; workarounds for the rest
Clock changes
No effect in the zones checked

Open in the tester

In each dialect

Every expression here is generated by the tester’s own engine and checked run for run against the others, so “Exact” means the same clock times in the same zone. Each one links to the tester.

Can’t be written in Cron

Standard cron has no 1W. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.

Workaround

Schedule 0 9 1-3 * * (at 09:00, on the 1st through the 3rd of the month) and let a shell test go ahead only when it is a weekday and either the 1st or a Monday. In a crontab, % must be written \%, or cron cuts the line there.

0 9 1-3 * * w=$(date +\%u); [ $w -le 5 ] && { [ $(date +\%-d) -eq 1 ] || [ $w -eq 1 ]; } && /path/to/command
No equivalent
aws events put-rule --name first-weekday-of-month --schedule-expression "cron(0 9 1W * ? *)"
  • AWS does not say what W does at the edges of a month. This follows Quartz, which never crosses a month boundary.
  • Wrap it in cron(…). Day of month is set (1W), so day of week has to be ?. The final * is the required year field.
  • Scheduled rules always run in UTC. For local times, create it in EventBridge Scheduler with --schedule-expression-timezone.
Exact
CronScheduleBuilder.cronSchedule("0 0 9 1W * ?")
  • Runs follow Quartz’s rule that W never crosses a month boundary.
  • The leading 0 is the seconds field, so it fires at the top of the minute. Day of month is set (1W), so day of week has to be ?. The optional year is left off.
  • Uses the JVM’s default zone unless the trigger sets one with inTimeZone.
Exact
Can’t be written in Kubernetes

Kubernetes CronJob has no 1W. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.

Workaround

Schedule 0 9 1-3 * * and put the test in front of the container’s command, so the Job exits straight away unless it is a weekday and either the 1st or a Monday. The test uses GNU date, so Alpine and other BusyBox images need coreutils. The container’s clock is UTC unless you set TZ; match it to timeZone.

schedule: "0 9 1-3 * *"
# in the container:
command: ["sh", "-c", "w=$(date +%u); [ $w -le 5 ] && { [ $(date +%-d) -eq 1 ] || [ $w -eq 1 ]; } && exec /app/job"]
No equivalent
Can’t be written in GitHub

GitHub Actions has no 1W. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.

Workaround

Schedule 0 9 1-3 * * and make the test the first line of the step that does the work, so the step ends early unless it is a weekday and either the 1st or a Monday. The runner’s clock is UTC; if you set timezone, give the step the same zone with env: TZ.

- cron: '0 9 1-3 * *'
# in the job:
- run: |
    w=$(date +%u); [ $w -le 5 ] && { [ $(date +%-d) -eq 1 ] || [ $w -eq 1 ]; } || exit 0
    ./your-script
No equivalent
Can’t be written in Azure

Azure Functions has no 1W. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.

Workaround

Schedule 0 0 9 1-3 * * and return early unless it is a weekday and either the 1st or a Monday. UtcNow matches the default UTC schedule; if you set WEBSITE_TIME_ZONE, compare local time instead.

[TimerTrigger("0 0 9 1-3 * *")]
// first line of the function:
var t = DateTime.UtcNow; if (t.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday || (t.Day != 1 && t.DayOfWeek != DayOfWeek.Monday)) return;
No equivalent

About this schedule

The first Monday-to-Friday day of the month: the 1st, or Monday the 2nd or 3rd when the 1st falls at a weekend. Quartz writes it 1W, the weekday nearest the 1st, which never crosses into the previous month. Holidays are not considered by any of these schedulers.

Clock changes

None of these times falls in the hour that clocks skip or repeat in New York, London, Berlin, Helsinki or Sydney, so every dialect runs it normally all year there. Zones that change clocks at other hours can still differ.

Questions

What is the cron expression for the first weekday of the month?
It is 0 9 1W * ? * in EventBridge and 0 0 9 1W * ? in Quartz. Standard cron, Kubernetes, GitHub Actions and Azure cannot express it in one line, so this page gives a tested workaround for each.
Can a Kubernetes CronJob run on the first weekday of the month?
Not with the schedule alone. Schedule 0 9 1-3 * * and put the test in front of the container’s command, so the Job exits straight away unless it is a weekday and either the 1st or a Monday. The test uses GNU date, so Alpine and other BusyBox images need coreutils. The container’s clock is UTC unless you set TZ; match it to timeZone.

More schedules