← Back to Blog

Cron Expression Deep Dive: Fields, Special Characters, Common Patterns & Troubleshooting

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

  1. Specifying both day and week: Standard Cron treats them as OR; Quartz requires ? on one
  2. Different week numbering: Standard Cron's 0 and 7 are both Sunday; Quartz's 1 is Sunday, 7 is Saturday
  3. 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 support 0-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:

Best Practices

  1. Specify timezone: Always document the timezone in config and docs
  2. Avoid boundary times: Don't use exact midnight (0 0 0 * * *) — multiple services firing simultaneously causes load spikes. Add a random offset
  3. Comment clearly: Add comments explaining each cron expression's trigger logic
  4. Design for idempotency: Task logic should be idempotent to prevent data issues from duplicate runs
  5. Set timeouts and retries: Prevent stuck tasks from blocking subsequent schedules
  6. Monitor and alert: Set up failure alerts for critical scheduled tasks
← Back to Blog