Every 15th of the Month Cron Expression
Runs on the 15th of every month at midnight.
0 0 15 * * in POSIX cron
Expression
0 0 15 * *Try it in the tester →Field Breakdown
How It Works
Day-of-month pinned to 15 — fires once per month at 00:00 on the 15th, regardless of weekday.
Example Run Times
- Jan 15 00:00:00
- Feb 15 00:00:00
- Mar 15 00:00:00
- Apr 15 00:00:00
- May 15 00:00:00
Frequently Asked Questions
Common use cases?
Mid-month payroll reminders, bi-monthly billing reconciliation paired with the 1st, and subscription mid-cycle invoice generation.
0 0 15 * * in Quartz / Spring
Midnight on the 15th of every month — twelve firings a year, always near the month's midpoint. The 15 sits in day-of-month, so the mandatory ? moves to day-of-week, marking this as a date-driven schedule.
Quartz / Spring expression
0 0 0 15 * ?Try it in the tester →At 00:00:00 on day 15 of the month
Unix / POSIX equivalent: 0 0 15 * *
Field Breakdown
Using It with Quartz / Spring
The 15th is the traditional mid-cycle checkpoint: mid-month invoicing runs, halfway-point budget alerts, and retention sweeps that shouldn't wait for month-end all live here. Spring schedules it with @Scheduled(cron = "0 0 0 15 * ?"); Quartz with the same string on a CronTrigger. One pleasant property over higher day numbers: every month has a 15th, so this trigger fires exactly twelve times a year with no February asterisk — something a schedule pinned to the 30th cannot claim.
Quartz-Specific Notes
- →Quartz's W modifier turns this into a business-day variant: 15W in the day-of-month field means the weekday nearest the 15th, shifting a Saturday occurrence back to Friday the 14th and a Sunday one forward to Monday the 16th.
- →Quartz refuses expressions with concrete values in both day fields, so there is no way to write "the 15th, but only if it's a Tuesday" in one expression — that test belongs in job code.
- →The interval between consecutive firings varies with month length: 28 days out of February's 15th, 31 days out of January's. Treat it as monthly, not as every-30-days.
Frequently Asked Questions
What if the 15th lands on a weekend and the job needs a business day?
Replace 15 with 15W: 0 0 0 15W * ? fires on the closest weekday to the 15th — the 14th when the 15th is a Saturday, the 16th when it's a Sunday — always within the same month.
Can I require both the 15th and a specific weekday in one expression?
Not in Quartz — exactly one of day-of-month and day-of-week must be ?, so the two can never be combined. Fire on the 15th and check the weekday inside the job, returning early when it doesn't match.
0 0 15 * * in AWS EventBridge
Twelve runs a year, at 00:00 UTC as the 15th of each month begins. The 15th is the safest mid-month anchor in cron — every month has one, so the cadence never silently skips the way a 29th, 30th, or 31st schedule would.
AWS EventBridge expression
cron(0 0 15 * ? *)Try it in the tester →At 00:00 UTC on day 15 of the month
Unix / POSIX equivalent: 0 0 15 * *
Field Breakdown
Using It with AWS EventBridge
Use it for work that belongs at the half-way checkpoint of a monthly cycle: a Lambda that compares month-to-date spend from Cost Explorer against budget and flags accounts trending over before month-end surprises anyone, mid-cycle invoice reminders for net-30 customers, or a data-quality audit that samples the month's records while there is still time to fix the pipeline writing them. The day-of-month field carries the constraint here, so the mandatory ? moves to day-of-week.
AWS-Specific Notes
- →Which weekday the 15th falls on rotates month to month, so any human-facing output from this schedule will sometimes land on a Saturday or Sunday.
- →If weekend fires are unacceptable, EventBridge's W token solves it in place: cron(0 0 15W * ? *) shifts to the nearest weekday — the 14th when the 15th is a Saturday, the 16th when it is a Sunday.
- →Twelve invocations a year leave little operational signal; alarm on the rule's FailedInvocations metric rather than waiting to notice a missing month.
Frequently Asked Questions
Can the mid-month run happen at a civilized hour instead of midnight UTC?
Set the hour field: cron(0 14 15 * ? *) fires at 14:00 UTC on the 15th, which is morning across the US. For an exact local time across DST, use EventBridge Scheduler with a timezone.
What does 15W do in February when the 15th is a Sunday?
It fires on Monday the 16th. W always resolves to the closest weekday within the same month — it will never jump into the previous or following month.
0 0 15 * * in GitHub Actions
Day-of-month 15 with everything else pinned or open: twelve runs a year, each at 00:00 UTC on the 15th. The mid-month slot is the mirror image of first-of-the-month scheduling — same monthly rhythm, deliberately offset from the start-of-month cluster of bills, reports, and rotations.
GitHub Actions expression
0 0 15 * *Try it in the tester →At 00:00 UTC on day 15 of the month
Unix / POSIX equivalent: 0 0 15 * *
Field Breakdown
Using It with GitHub Actions
Placed in workflow YAML as on: schedule: - cron: "0 0 15 * *" on the default branch, the 15th works as a mid-cycle checkpoint: reconciling usage data halfway through a billing period, rotating a second set of credentials offset from the first-of-month set so no single day rotates everything, or producing a mid-month burn-down against monthly goals while there is still time to react. Because there are only twelve fire times a year, bundle workflow_dispatch into the trigger list so schedule changes can be exercised on demand instead of waiting weeks.
GitHub-Specific Notes
- →Every month has a 15th, so this never skips — unlike schedules pinned to days 29, 30, or 31, which silently miss the months that lack those dates.
- →The weekday floats: the 15th will land on a Saturday or Sunday roughly two months in every seven, so workflows that open work for humans should decide what a weekend 15th means.
- →At 00:00 UTC on the 15th, every timezone west of UTC is still living the 14th — date-stamped output should be computed in UTC to avoid an off-by-one against local expectations.
Frequently Asked Questions
Does a day-of-month schedule like this ever skip a month?
Not this one — the 15th exists in all twelve months. Skipping only afflicts day values 29 through 31; February simply never matches 30 or 31, and matches 29 only in leap years.
Can I run on the 15th, or the nearest weekday if it falls on a weekend?
Not in the expression — that is Quartz's W token, which GitHub's five-field cron does not implement. Keep the schedule fixed on the 15th and put the weekday awareness in the job: run immediately on weekdays, or set a delayed follow-up for Monday when date +%u prints 6 or 7.