PatternCron
Last day of the month (the safe way)
Cron has no native 'last day' syntax. This fires on the 28th through 31st and relies on a guard clause to no-op on any day but the real last one.
Last updated
In plain English
- Fires at 23:55 on the 28th, 29th, 30th and 31st
- Needs an in-script guard to isolate the true last day
Why it’s written this way
28-31 in the day-of-month field is a range: the job launches on every one of those four dates, every month, at 23:55. That's deliberate — it's the only portable way to guarantee a hit on the true last day, since months vary between 28 and 31 days long.
The schedule alone can't finish the job: the actual 'is today the last day' decision has to live in the script itself, guarding the body with something like [ "$(date -d tomorrow +%d)" = "01" ] || exit 0 so only the genuine last-day run does anything.
Edge cases to know
- →Skip the guard clause and the job runs its full logic on the 28th, 29th, 30th AND 31st in any month with 31 days — for anything non-idempotent, like charging a card or sending an invoice, that's up to three unwanted extra runs.
- →Some schedulers support smarter 'last day' syntax directly — Vixie cron's L is nonstandard and rarely available, but systemd timers' OnCalendar do support it. Prefer the native option if the target system has one.
- →The guard shown here is GNU date syntax (date -d); BSD and macOS date need a different flag (date -v+1d), so the exact one-liner isn't portable across every box the job might end up running on.