Cron Expression: Last Day of Month Workaround (59 23 28-31 * *)
Need to generate a cron expression?
Use CronOS to generate any cron expression you wish with natural language. Simply describe what you need, and we'll create the perfect cron expression for you. It's completely free!
Cron Expression: Last Day of Month Workaround (59 23 28-31 * *)
The cron expression 59 23 28-31 * * is a workaround for executing tasks on the last day of every month. Since standard cron doesn't support "last day" directly, this pattern runs on the 28th, 29th, 30th, and 31st, requiring script logic to determine the actual last day.
Expression Breakdown
59 23 28-31 * *
│ │ │ │ │ │
│ │ │ │ │ └─── Day of week: * (every day)
│ │ │ │ └───── Month: * (every month)
│ │ │ └─────── Day of month: 28-31 (range)
│ │ └─────────── Hour: 23 (at hour 23, 11:00 PM)
└─────────────── Minute: 59 (at minute 59)
Field Values
| Field | Value | Meaning |
|---|---|---|
| Minute | 59 | At minute 59 |
| Hour | 23 | At hour 23 (11:00 PM) |
| Day of Month | 28-31 | Days 28, 29, 30, or 31 |
| Month | * | Every month (1-12) |
| Day of Week | * | Every day of week (0-7) |
Range Syntax
The 28-31 in the day of month field is a range that means "days 28, 29, 30, or 31":
- Runs on: 28th, 29th, 30th, and 31st at 11:59 PM
- Requires script logic to check if it's actually the last day
Execution Time
This expression runs multiple times per month at:
- 28th, 29th, 30th, and 31st at 23:59 (11:59 PM)
Important: The script must check if the current day is actually the last day of the month.
Common Use Cases
1. End-of-Month Reports
59 23 28-31 * * /usr/local/bin/check-last-day.sh
Generate end-of-month reports on the actual last day.
2. End-of-Month Backups
59 23 28-31 * * /usr/local/bin/check-last-day.sh
Create end-of-month backups on the actual last day.
3. End-of-Month Billing
59 23 28-31 * * /usr/local/bin/check-last-day.sh
Process end-of-month billing on the actual last day.
Example Implementations
Last Day Check Script
#!/bin/bash
# /usr/local/bin/check-last-day.sh
# Get tomorrow's date
TOMORROW=$(date -d "tomorrow" +%Y-%m-%d)
TODAY=$(date +%Y-%m-%d)
# Extract day components
TODAY_DAY=$(date +%d)
TOMORROW_DAY=$(date -d "tomorrow" +%d)
# Check if tomorrow is the 1st (meaning today is last day)
if [ "$TOMORROW_DAY" = "01" ]; then
echo "$(date): Today is the last day of the month, running end-of-month tasks"
# Run end-of-month tasks
/usr/bin/python3 /scripts/generate-end-of-month-report.py
/usr/local/bin/end-of-month-backup.sh
else
echo "$(date): Today is not the last day of the month, skipping"
exit 0
fi
Python Last Day Check
# check-last-day.py
from datetime import datetime, timedelta
def is_last_day_of_month():
today = datetime.now()
tomorrow = today + timedelta(days=1)
# If tomorrow is the 1st, today is the last day
return tomorrow.day == 1
if is_last_day_of_month():
print(f"{datetime.now()}: Today is the last day of the month")
# Run end-of-month tasks
# generate_end_of_month_report()
# create_end_of_month_backup()
else:
print(f"{datetime.now()}: Today is not the last day of the month, skipping")
Best Practices
- Script Validation: Always check if today is actually the last day
- Error Handling: Implement comprehensive error handling and logging
- Locking: Use file locks to prevent concurrent execution
- Monitoring: Set up alerts for failed monthly jobs
- Edge Cases: Handle months with different numbers of days correctly
Limitations
- Multiple Executions: This pattern runs 4 times per month (28-31)
- Script Required: Must include logic to check for actual last day
- Not Ideal: Consider using a scheduling library that supports "last day" natively
Alternative Approaches
Using a Scheduling Library
Many modern scheduling libraries support "last day" directly:
// node-cron with last day support
const cron = require('node-cron');
cron.schedule('0 0 L * *', () => {
// Runs on last day of month
});
Using Day-of-Week Alternative
For some use cases, you might use the first day of next month minus 1 day:
# Run on 1st at 00:00, but process previous month's data
0 0 1 * * /usr/bin/python3 /scripts/process-last-month.py
When to Use
✅ Good for:
- End-of-month reports
- End-of-month backups
- End-of-month billing
- Monthly closure tasks
❌ Avoid for:
- Tasks requiring precise last-day execution without script logic
- Consider alternatives if possible
Related Patterns
| Pattern | Expression | Description |
|---|---|---|
| First day of month | 0 0 1 * * | Start of month |
| 15th of month | 0 0 15 * * | Mid-month |
| Last day workaround | 59 23 28-31 * * | End of month (with script) |
Conclusion
The 59 23 28-31 * * expression is a workaround for last-day-of-month operations. While it requires script logic to validate the actual last day, it's a practical solution when standard cron doesn't support "last day" directly. Consider using scheduling libraries that support last-day syntax for cleaner implementations.
Need to generate a cron expression?
Use CronOS to generate any cron expression you wish with natural language. Simply describe what you need, and we'll create the perfect cron expression for you. It's completely free!