Quarterly End-of-Period Job Schedule | CronBase
0 9 L 3,6,9,12 * At nine o'clock in the morning on the final day of March, June, September, and December.
This cron expression schedules a task to execute at exactly 9:00 AM on the last day of March, June, September, and December. It is designed for end-of-quarter workloads, such as financial closeouts, quarterly compliance reporting, data warehousing rollups, and seasonal system cleanup operations.
- Minute
- 0
- Hour
- 9
- Day of Month
- L
- Month
- 3,6,9,12
- Day of Week
- *
* Tools
Code & Implementations
#!/usr/bin/env bash
set -euo pipefail
# Standard cron doesn't natively support 'L'.
# Run this script at 9:00 AM on days 28-31 of March, June, September, December.
# Cron line: 0 9 28-31 3,6,9,12 *
TODAY=$(date +%d)
TOMORROW=$(date -d "tomorrow" +%d)
# If tomorrow's day of the month is 1, then today is the last day of the month
if [ "$TOMORROW" -eq 1 ]; then
echo "Executing quarterly batch processing job..."
# Insert production workload here
else
echo "Skipping: Today is day $TODAY, which is not the last day of the quarter."
exit 0
fi › Setup notes
Save this script as /usr/local/bin/quarterly_job.sh and schedule it in crontab using: 0 9 28-31 3,6,9,12 * /usr/local/bin/quarterly_job.sh
const cron = require('node-cron');
const { DateTime } = require('luxon');
// Run every day at 9:00 AM in March, June, September, and December
// and programmatically assert if it is the last day of the month.
cron.schedule('0 9 28-31 3,6,9,12 *', () => {
const now = DateTime.local().setZone('America/New_York');
const isLastDay = now.day === now.endOf('month').day;
if (isLastDay) {
console.log('Starting end-of-quarter execution at:', now.toString());
// Execute quarterly reconciliation
} else {
console.log('Skipping execution: not the last day of the month.');
}
}); › Setup notes
Install dependencies with 'npm install node-cron luxon'. Run with Node.js in a persistent background process.
import datetime
import sys
def is_last_day_of_month(dt):
next_day = dt + datetime.timedelta(days=1)
return next_day.day == 1
def run_quarterly_job():
today = datetime.datetime.now()
if not is_last_day_of_month(today):
print("Skipping: Today is not the end of the quarter.")
sys.exit(0)
print("Executing quarterly cleanup and reporting...")
# Production logic goes here
if __name__ == "__main__":
run_quarterly_job() › Setup notes
Schedule this script to run daily at 9 AM during target months: 0 9 28-31 3,6,9,12 * python3 quarterly_job.py
package main
import (
"fmt"
"time"
)
func isLastDayOfMonth(t time.Time) bool {
oneDayLater := t.AddDate(0, 0, 1)
return oneDayLater.Day() == 1
}
func main() {
now := time.Now()
if isLastDayOfMonth(now) {
fmt.Println("Running quarterly ledger consolidation...")
// Execute production tasks
} else {
fmt.Println("Skipping run; not the last day of the quarter.")
}
} › Setup notes
Compile the binary and schedule it to run daily at 9 AM during end-of-quarter months using standard OS scheduler.
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.logging.Logger;
@Component
public class QuarterlyJobScheduler {
private static final Logger LOGGER = Logger.getLogger(QuarterlyJobScheduler.class.getName());
// Spring Cron supports the 'L' character natively for the day-of-month field.
// Format: second minute hour day-of-month month day-of-week
@Scheduled(cron = "0 0 9 L 3,6,9,12 *")
public void executeQuarterlyTask() {
LOGGER.info("Executing native Spring quarterly job at " + LocalDate.now());
// Run enterprise report generation
}
} › Setup notes
Ensure @EnableScheduling is declared on your Spring Boot application configuration class to activate this scheduled task.
apiVersion: batch/v1
kind: CronJob
metadata:
name: quarterly-cleanup-job
spec:
# Standard K8s cron parser doesn't support 'L'.
# We run daily on 28-31 of target months and validate via a wrapper.
schedule: "0 9 28-31 3,6,9,12 *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: worker
image: alpine:latest
command:
- /bin/sh
- -c
- |
TOMORROW=$(date -d "+1 day" +%d)
if [ "$TOMORROW" -ne 1 ]; then
echo "Not the last day of the quarter. Exiting."
exit 0
fi
echo "Starting quarterly data-pipeline sync..."
# Run execution script here
restartPolicy: OnFailure › Setup notes
Apply this manifest using 'kubectl apply -f quarterly-cronjob.yaml' in your Kubernetes cluster.
Last verified:
Monitor this schedule in production
Get alerted the moment this cron job fails, is late, or doesn't run. BetterStack tracks execution, duration, and output — no infrastructure required.
Platform Equivalents
Systemd Timer
OnCalendar*-03,06,09,12-* 09:00:00
[Unit]
Description=Timer for cron expression: 0 9 L 3,6,9,12 *
[Timer]
OnCalendar=*-03,06,09,12-* 09:00:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
Does standard Linux crontab support the 'L' character?
No, standard POSIX or Vixie cron does not recognize 'L'. Attempting to use it will result in a syntax error. To achieve this, you must schedule the job to run daily during the end of the month (days 28-31) and use a shell wrapper to evaluate if tomorrow is the first of the next month.
How do daylight saving time transitions affect a 9:00 AM schedule?
Since 9:00 AM is well outside the typical 1:00 AM to 3:00 AM DST shift window, the job is highly stable and will not be skipped or executed twice during clock changes. However, ensuring your system clock or container engine is set to a consistent timezone like UTC is highly recommended.
What happens if the last day of the quarter falls on a weekend?
This schedule executes on the last calendar day regardless of whether it is a business day or weekend. If your business logic requires execution on the last weekday of the quarter, you must programmatically shift the execution date or use an enterprise scheduler that supports business calendar modifiers.
How can I safely test this quarterly job without waiting three months?
You should decouple your business logic from the scheduler. Test the execution script independently by mocking the system date using tools like 'libfaketime' in Linux or overriding the current date parameter in your application code during integration testing.
How should I handle execution failures for such an infrequent job?
Because this job runs only four times a year, a failure can go unnoticed for months. You must implement active dead-man's snitches (e.g., Healthchecks.io) and direct PagerDuty or Slack alerts on failure. Ensure the job is idempotent so it can be safely retried manually if it fails midway.
* Explore
Related expressions you might need
Last verified: