Cron on the 1st and 15th: 0 0 1,15 * *
0 0 1,15 * * runs twice a month, on the 1st and the 15th, 24 times a year. The interval varies between 13 and 17 days depending on the month.
At a glance
- Expression
- 0 0 1,15 * *
- Fires
- 24 times a year, twice per calendar month.
- Restricted fields
- Day of month restricted to two values, minute and hour pinned. Month and day of week open.
- Equivalent macro
- None.
- Days matched
- 1 and 15.
- Gap range
- 14 days from the 1st to the 15th, then 13 to 17 days to the next 1st depending on month length.
In detail
Semi monthly is the schedule payroll and billing systems actually run on, and the comma list expresses it directly. Both days exist in every month, including February, so unlike a schedule pinned to the 30th or 31st this one never skips.
The gaps are not equal and this is the detail that matters. From the 1st to the 15th is always fourteen days. From the 15th to the next 1st is seventeen days after a 31 day month, sixteen after a 30 day month, fourteen after a 29 day February and thirteen after a 28 day one. Anything computing a period from a fixed lookback will be wrong for one of the two runs every month.
Because it is not a fortnight, the annual count is 24 rather than 26. Over a year that is two runs fewer than a true fourteen day cycle, which is the number to check if a contract, a subscription or an SLA is written in weeks rather than in months.
The most common variant is the 1st and 16th, which splits a 31 day month exactly in half and keeps the first gap at fifteen days. Neither is better in the abstract; pick the one that matches how the periods are defined elsewhere, and write it down where the next person will find it.
Examples
0 0 1,15 * *0 0 1,16 * *Fifteen days from the 1st to the 16th in a 31 day month.
0 6 1,15 * * /opt/app/bin/payroll-export.sh >> /var/log/payroll.log 2>&1Things that catch people out
- This is twice a month, not every two weeks. Over a year it is 24 runs against 26.
- The second gap of the month changes with the month length, so a fixed fourteen day lookback double counts or misses days.
- 0 0 1,15 * * and 0 0 */15 * * look similar and differ. The step form also matches day 31.
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 day of the month: 0 0 1 * *
0 0 1 * * runs at midnight on the 1st of every month, twelve times a year, and is exactly what the @monthly macro expands to.
Cron every 2 weeks: what cron cannot do and what to write
Cron has no fortnight. The two honest options are a semi monthly 1,15 schedule or a weekly job with a week parity test in the script.
Cron every 15 days: 0 0 */15 * * gives days 1, 16 and 31
0 0 */15 * * matches days 1, 16 and 31, so 31 day months get three runs and the gap from the 31st to the 1st is a single day.
The cron comma: listing several values in one field
A comma joins several values into one field, and each element can itself be a value, a range or a step. Spaces inside a list break the expression.
Cron on the 31st: 0 0 31 * * only fires seven times a year
0 0 31 * * runs only in months that have 31 days, so it fires seven times a year, not twelve. The same trap hits day 30 and day 29.
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.
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