What is a Cron Expression?
A cron expression is a string that defines when a scheduled task should run. Originally from the Unix cron daemon, it's now used across scheduling systems — from Linux crontab to Spring's @Scheduled, Quartz Scheduler, Kubernetes CronJob, and more.
A cron expression consists of 5 to 7 fields, each representing a time unit.
Field Structure
Standard 5-Field Cron
* * * * *
│ │ │ │ │
│ │ │ │ └── Day of week (0-7, both 0 and 7 = Sunday)
│ │ │ └──── Month (1-12)
│ │ └────── Day of month (1-31)
│ └──────── Hour (0-23)
└─────────── Minute (0-59)
Quartz 6/7-Field Cron
Quartz Scheduler uses 6 or 7 fields, adding seconds and an optional year:
* * * * * * *
│ │ │ │ │ │ │
│ │ │ │ │ │ └── Year (optional, 1970-2099)
│ │ │ │ │ └──── Day of week (1-7, 1=Sunday)
│ │ │ │ └────── Month (1-12)
│ │ │ └──────── Day of month (1-31)
│ │ └────────── Hour (0-23)
│ └──────────── Minute (0-59)
└─────────────── Second (0-59)
Field Reference Table
| Field | Standard Cron Range | Quartz Range | Allowed Special Chars |
|---|---|---|---|
| Second | — | 0-59 | , - * / |
| Minute | 0-59 | 0-59 | , - * / |
| Hour | 0-23 | 0-23 | , - * / |
| Day | 1-31 | 1-31 | , - * ? L W C |
| Month | 1-12 or JAN-DEC | 1-12 or JAN-DEC | , - * / |
| Day of week | 0-7 or SUN-SAT | 1-7 or SUN-SAT | , - * ? L C # |
| Year | — | 1970-2099 | , - * / |
Special Characters Explained
* — Any Value
Matches all valid values for the field. * * * * * means every minute.
, — List
Specifies multiple discrete values:
0 0 9,12,18 * * ? // Run at 9:00, 12:00, and 18:00 daily
- — Range
Specifies a continuous interval:
0 0 9-17 * * ? // Run every hour from 9:00 to 17:00
/ — Step
start/step — starting from start, execute every step:
0 */15 * * * ? // Every 15 minutes
0 0/30 * * * ? // At 0 and 30 minutes past the hour
? — No Specific Value (Quartz only)
Used only in day-of-month and day-of-week fields. Since these two fields can conflict, Quartz requires one to be ? when the other is specified.
0 0 12 ? * MON // Every Monday at noon (day field is ?)
0 0 12 1 * ? // 1st of every month at noon (week field is ?)
L — Last (Quartz only)
- In day field: last day of the month
- In week field: last specified weekday of the month
0 0 23 L * ? // Last day of month at 23:00
0 0 10 ? * 6L // Last Friday of month at 10:00
W — Nearest Weekday (Quartz only)
In the day field, finds the nearest weekday (Mon-Fri) to the given date:
0 0 9 15W * ? // Nearest weekday to the 15th at 9:00
If the 15th is Saturday, runs on the 14th (Friday); if Sunday, runs on the 16th (Monday).
# — Nth Day of Week (Quartz only)
dayOfWeek#nth:
0 0 10 ? * 2#1 // First Monday of the month at 10:00
0 0 10 ? * 6#3 // Third Friday of the month at 10:00
Common Scheduling Patterns
High-Frequency Tasks
| Expression | Description |
|---|---|
* * * * * |
Every minute |
*/5 * * * * |
Every 5 minutes |
0 * * * * |
Every hour on the hour |
0 */2 * * * |
Every 2 hours |
Daily Tasks
| Expression | Description |
|---|---|
0 0 * * * |
Every day at midnight |
0 9 * * * |
Every day at 9 AM |
0 9 * * 1-5 |
Weekdays at 9 AM |
0 22 * * 1-5 |
Weekdays at 10 PM |
Periodic Tasks
| Expression | Description |
|---|---|
0 0 1 * * |
1st of every month |
0 0 1 1 * |
January 1st every year |
0 0 * * 0 |
Every Sunday |
0 0 1 */3 * |
First day of each quarter |
Quartz-Specific Patterns
| Expression | Description |
|---|---|
0 0 12 ? * MON-FRI |
Weekdays at noon (Quartz format) |
0 0 23 L * ? |
Last day of month at 23:00 |
0 0 9 15W * ? |
Nearest weekday to 15th at 9:00 |
0 0 10 ? * 6#3 |
Third Friday at 10:00 |
0 30 10-14 ? * MON,WED,FRI |
Mon/Wed/Fri, 10:30-14:30 hourly |
Quartz vs Standard Cron: Key Differences
| Feature | Standard Cron | Quartz |
|---|---|---|
| Field count | 5 | 6 or 7 |
| Second precision | ❌ | ✅ |
| Year field | ❌ | ✅ (optional) |
? character |
❌ | ✅ (required for day/week) |
L character |
❌ | ✅ |
W character |
❌ | ✅ |
# character |
❌ | ✅ |
| Week encoding | 0=Sun, 7=Sun | 1=Sun, 7=Sat |
| Day & week relationship | OR (either matches) | One must be ? |
Common Pitfalls
- Specifying both day and week: Standard Cron treats them as OR; Quartz requires
?on one - Different week numbering: Standard Cron's
0and7are both Sunday; Quartz's1is Sunday,7is Saturday - Seconds field: Forgetting to add it when migrating from standard to Quartz
Troubleshooting Guide
1. Expression Not Firing
Checklist:
- Is the timezone correct? Quartz defaults to JVM timezone
- Are day and week fields conflicting? Quartz requires one to be
? - Is step syntax correct? Use
*/15, not all implementations support0-59/15 - Is the date valid?
0 0 0 31 2 *never fires (Feb has no 31st)
2. Unexpected Frequency
# Intent: every 5 minutes
*/5 * * * * # ✅ Correct: 0, 5, 10, 15...
0/5 * * * * # ⚠️ Equivalent in some systems, not all
0-59/5 * * * * # ✅ Explicit range with step
3. Timezone Issues
- Linux cron uses the server's local timezone
- Quartz can set timezone via
CronTrigger - Kubernetes CronJob uses UTC — calculate accordingly
// Quartz with explicit timezone
CronTrigger trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 9 * * ?")
.inTimeZone(TimeZone.getTimeZone("America/New_York")))
.build();
4. February 29th Problem
0 0 0 29 2 * only fires on leap years. For annual tasks, use program logic instead of relying on cron.
5. Daylight Saving Time
In DST-observing timezones, certain times are skipped or repeated during transitions. Recommendations:
- Avoid scheduling critical tasks between 2:00-3:00 AM
- Use UTC for scheduling
Online Validation Tools
Validate before deploying:
- crontab.guru — Standard 5-field cron
- CronMaker — Quartz 7-field cron
- FreeFormatter Cron Validator
Best Practices
- Specify timezone: Always document the timezone in config and docs
- Avoid boundary times: Don't use exact midnight (
0 0 0 * * *) — multiple services firing simultaneously causes load spikes. Add a random offset - Comment clearly: Add comments explaining each cron expression's trigger logic
- Design for idempotency: Task logic should be idempotent to prevent data issues from duplicate runs
- Set timeouts and retries: Prevent stuck tasks from blocking subsequent schedules
- Monitor and alert: Set up failure alerts for critical scheduled tasks