Cron on the last Friday of the month: no # or L support
There is no last Friday syntax in standard cron, and the Quartz L and # characters are rejected. Schedule every Friday and have the script exit unless the next Friday falls in a different month.
At a glance
- Expression
- 0 17 * * 5 with an end of month guard
- Fires
- 12 times a year of real work, out of 52 or 53 scheduled Friday runs.
- Restricted fields
- Day of week pinned to Friday, minute and hour pinned. The end of month test lives in the script.
- Equivalent macro
- None.
- Quartz equivalent
- 6L in Quartz means the last Friday. This parser rejects L with a named error.
- Useful runs versus scheduled runs
- About one in four Friday runs does the work.
In detail
The last Friday of a month can be any date from the 22nd to the 31st, so no day of month range pins it, and restricting both date fields would trigger the OR rule and make things worse. The dialect that solves this properly is Quartz, where 6L means the last Friday, but this parser rejects L and reports it as unsupported rather than guessing.
The approach that does work is to schedule every Friday, which is a single clean expression with only the day of week field restricted, and then ask a simple question in the script: is the Friday seven days from now in a different month? If it is, today is the last Friday. If it is not, exit.
Comparing the month number of today plus seven days against the month number of today is enough. It handles every month length, it handles February, it handles leap years and it handles the December to January rollover, because the month numbers differ in every one of those cases.
Roughly one Friday in four does real work, so the other three or four runs a month have to be cheap. Keep the guard as the first thing the script does, before it opens a connection or reads configuration, and the wasted runs cost a process spawn and nothing else.
Examples
0 17 * * 5 [ "$(date -d '+7 days' +\%m)" != "$(date +\%m)" ] && /opt/app/bin/month-end.shdate -d is a GNU extension. On BSD userlands use date -v+7d instead.
0 17 * * 6LThe parser reports that L, W, # and ? are not supported in the day of week field.
0 17 1-7 * * [ "$(date +\%u)" = "5" ] && /opt/app/bin/month-start.shThe first Friday is always in days 1 to 7, so a date range plus a weekday test is enough.
Things that catch people out
- 0 17 22-31 * 5 looks like a narrowing but is not. Restricting both date fields makes cron fire when either matches.
- The guard must run before anything expensive, because three quarters of the scheduled runs will fail it.
- 6#5 is not the last Friday even in Quartz. It means the fifth Friday, which does not exist in most months.
Try it: Cron Expression Parser
Paste any five or six field expression to get a plain English description, a per field breakdown and the next run times in your local time.
Cron Expression Parser
LOCAL · Browser-OnlyParse cron expressions and see the next scheduled run times
Supports 5-field (minute hour day month weekday), 6-field (with seconds), named values (JAN, MON), and @macros (@daily, @hourly, ...)
Use the dropdowns below to build your cron expression visually, or type a custom value in each field.
Human-Readable Description
Every 5 minutes
Field Breakdown
| Field | Allowed | Value | Meaning |
|---|---|---|---|
| Minute | 0-59 | */5 | every 5 minute(s) |
| Hour | 0-23 | * | every hour |
| Day of Month | 1-31 | * | every day of month |
| Month | 1-12 | * | every month |
| Day of Week | 0-7 | * | every day of week |
Related cron expressions
All cron expressions →Cron on the first Monday of the month: the OR rule trap
0 9 1-7 * 1 does not mean the first Monday. Because both date fields are restricted, it fires on days 1 to 7 and on every Monday as well.
Cron L, W, # and ?: the Quartz characters this parser rejects
The Quartz extensions L, W, # and ? are rejected here with a named error. What each one means in Quartz, and what to write in standard cron instead.
Cron on the last day of the month: the L workaround
This parser rejects the Quartz L character, so the last day of the month needs a date range plus a script guard that checks whether tomorrow is the 1st.
Cron day of month and day of week: the OR rule
When both date fields are restricted, cron fires if either matches, not both. This turns narrowing expressions into much wider ones without any error.
Cron every 2 months: 0 0 1 */2 * and the odd month set
0 0 1 */2 * fires on the 1st of January, March, May, July, September and November. Use 0 0 1 2/2 * for the even months instead.
Cron every quarter: 0 0 1 */3 *
0 0 1 */3 * runs on the 1st of January, April, July and October, four times a year. Shift it with 2/3 for a fiscal year starting in February.
Tools for this
- Cron Expression Parser Free online cron expression parser - parse cron expressions and see the next scheduled run times
- Crontab Generator Free online crontab generator - build cron expressions visually with an intuitive interface