Cron expression for the last weekday of the month
At 09:00, on the last weekday 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
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.
Standard cron has no LW. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.
Workaround
Schedule 0 9 * * 1-5 (at 09:00, Monday through Friday) and let a shell test go ahead only when the next weekday falls in another month. In a crontab, % must be written \%, or cron cuts the line there.
0 9 * * 1-5 n=1; [ $(date +\%u) -eq 5 ] && n=3; [ "$(date -d "+$n days" +\%m)" != "$(date +\%m)" ] && /path/to/commandaws events put-rule --name last-weekday-of-month --schedule-expression "cron(0 9 LW * ? *)"- AWS’s docs list
LandWbut not the combinedLW. Runs here follow Quartz’s “last weekday of the month”. Confirm in the console. - Wrap it in
cron(…). Day of month is set (LW), 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.
CronScheduleBuilder.cronSchedule("0 0 9 LW * ?")- The leading
0is the seconds field, so it fires at the top of the minute. Day of month is set (LW), 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.
Kubernetes CronJob has no LW. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.
Workaround
Schedule 0 9 * * 1-5 and put the test in front of the container’s command, so the Job exits straight away unless the next weekday falls in another month. 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-5"
# in the container:
command: ["sh", "-c", "n=1; [ $(date +%u) -eq 5 ] && n=3; [ \"$(date -d \"+$n days\" +%m)\" != \"$(date +%m)\" ] && exec /app/job"]GitHub Actions has no LW. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.
Workaround
Schedule 0 9 * * 1-5 and make the test the first line of the step that does the work, so the step ends early unless the next weekday falls in another month. The runner’s clock is UTC; if you set timezone, give the step the same zone with env: TZ.
- cron: '0 9 * * 1-5'
# in the job:
- run: |
n=1; [ $(date +%u) -eq 5 ] && n=3; [ "$(date -d "+$n days" +%m)" != "$(date +%m)" ] || exit 0
./your-scriptAzure Functions has no LW. The last or nearest weekday changes from month to month, and plain cron fields cannot follow it.
Workaround
Schedule 0 0 9 * * Mon-Fri and return early unless the next weekday falls in another month. UtcNow matches the default UTC schedule; if you set WEBSITE_TIME_ZONE, compare local time instead.
[TimerTrigger("0 0 9 * * Mon-Fri")]
// first line of the function:
var t = DateTime.UtcNow; if (t.AddDays(t.DayOfWeek == DayOfWeek.Friday ? 3 : 1).Month == t.Month) return;About this schedule
The last Monday-to-Friday day of the month, often the last working day: the last day itself, or the Friday before when the month ends at a weekend. Quartz writes it LW. AWS documents L and W but not the two together, so the EventBridge form is marked as unconfirmed.
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 last weekday of the month?
- It is
0 9 LW * ? *in EventBridge and0 0 9 LW * ?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 last weekday of the month?
- Not with the schedule alone. Schedule
0 9 * * 1-5and put the test in front of the container’s command, so the Job exits straight away unless the next weekday falls in another month. The test uses GNUdate, so Alpine and other BusyBox images needcoreutils. The container’s clock is UTC unless you setTZ; match it totimeZone.
Related schedules
- 0 0 0 L * ?Quartz; workaround for cronLast day of the monthAt 00:00, on the last day of the month
- 0 0 9 1W * ?Quartz; workaround for cronFirst weekday of the monthAt 09:00, on the weekday nearest the 1st of the month
- 0 0 9 ? * 6LQuartz; workaround for cronLast Friday of the monthAt 09:00, on the last Friday of the month