Comparison

Quartz vs Unix cron.

Quartz and Unix cron look almost identical at first glance, but they have different field counts, different special characters, and different rules for when one day field interacts with the other. This guide explains every difference and how to migrate expressions between them.

The 30-second overview

Quartz is a Java-world scheduling library used inside Spring, JBoss, and various enterprise frameworks. Unix cron is the OS-level scheduler on Linux/macOS/BSD. They share a syntactic ancestor — Vixie cron from 1987 — but Quartz added features for finer scheduling.

Unix cronQuartz
Fields56 (year optional, → 7)
Seconds fieldNoYes (leftmost)
Year fieldNoOptional
Special chars* , - /* , - / ? L W #
Day-of-week start0=Sunday1=Sunday
Macros@daily, @hourly, etc.None (use explicit form)
Day-of-month/week both set?OR (either matches)One must be ?

Field count: 5 vs 6/7

Unix cron — 5 fields

┌────────── minute (0-59)
│ ┌──────── hour (0-23)
│ │ ┌────── day of month (1-31)
│ │ │ ┌──── month (1-12)
│ │ │ │ ┌── day of week (0-6, Sun=0)
* * * * *

Reading "minute hour day-of-month month day-of-week" left-to-right.

Quartz — 6 or 7 fields

┌───────────── second (0-59)
│ ┌─────────── minute (0-59)
│ │ ┌───────── hour (0-23)
│ │ │ ┌─────── day of month (1-31)
│ │ │ │ ┌───── month (1-12)
│ │ │ │ │ ┌─── day of week (1-7, Sun=1)
│ │ │ │ │ │ ┌─ year (optional, 1970-2099)
* * * * * *  *

Quartz adds a seconds field on the left and an optional year on the right. The day-of-week numbering is offset by one (Quartz: 1=Sunday; Unix: 0=Sunday).

Special characters Quartz adds

? — No specific value

Used in day-of-month or day-of-week fields to mean "no value here, use the other day field." In Quartz, you can't set both day fields meaningfully — one must be ?:

0 0 9 ? * MON-FRI    # Every weekday at 9 AM (Quartz)
0 0 9 15 * ?         # Every 15th of the month at 9 AM (Quartz)

L — Last

In day-of-month: the last day of the month (whether 28, 29, 30, or 31).
In day-of-week: the last weekday of the month (Saturday by default; 6L = last Friday).

0 0 0 L * ?          # Midnight on the last day of every month
0 0 0 ? * 6L         # Midnight on the last Friday of every month

W — Nearest weekday

In day-of-month: the weekday nearest to the given date. If the date falls on a weekend, fires on the closest weekday instead.

0 0 9 15W * ?        # At 9 AM on the weekday closest to the 15th
                     # (if 15th is Sat, fires Fri 14th; if Sun, fires Mon 16th)

# — Nth weekday of the month

Used in day-of-week. Format: dayOfWeek#n where n is which occurrence.

0 0 9 ? * 2#1        # 9 AM on the first Monday of every month
0 0 14 ? * 6#3       # 2 PM on the third Friday of every month

(In Quartz, Monday=2, since Sunday=1.)

The day-field interaction

This is the single biggest difference in behavior.

Unix cron: OR semantics

When both day-of-month and day-of-week are restricted (neither is *), Unix cron fires when EITHER matches. Example:

0 0 15 * 1   # Midnight on the 15th, AND midnight on every Monday

This is rarely what users want. To get "midnight on the 15th, only if it's a Monday," you have to gate inside your script.

Quartz: exactly one of the two must be ?

Quartz refuses to interpret two restricted day fields. You're forced to pick one as the "active" day field and mark the other ?:

0 0 0 15 * ?         # OK: Midnight on the 15th (ignore day-of-week)
0 0 0 ? * MON        # OK: Midnight every Monday (ignore day-of-month)
0 0 0 15 * MON       # ERROR: ambiguous
0 0 0 * * *          # ERROR: at least one day field must be ?

This is annoying for new users but eliminates the OR-vs-AND confusion entirely.

Migrating expressions

Unix → Quartz

Add a leading 0 for seconds, replace one day field with ?, and (if day-of-week is set) shift the numbers up by 1.

UnixQuartz equivalent
0 9 * * 1-50 0 9 ? * MON-FRI
*/5 * * * *0 0/5 * * * ?
0 0 1 * *0 0 0 1 * ?
0 22 * * 50 0 22 ? * FRI

Quartz → Unix

Drop the seconds field, replace ? with *, shift day-of-week down by 1.

If the Quartz expression uses L, W, or #, there's no exact Unix equivalent. Workarounds:

  • L (last day of month): use 28-31 in day-of-month, then check [[ $(date -d tomorrow +\%d) == 01 ]] in your script
  • 15W: use 14,15,16 in day-of-month and gate on weekday inside the script
  • 2#1 (first Monday): use 1-7 in day-of-month and 1 in day-of-week

Which should you use?

It usually isn't your choice — the framework picks for you:

If you're using…You get…
Linux/macOS crontab, systemd, anacronUnix cron
Spring @Scheduled, Quartz SchedulerQuartz (or Spring's variant)
AWS EventBridgeQuartz-like with year
GitHub Actions, Kubernetes CronJobUnix cron
Azure Functions TimerNCRONTAB (Unix-like with seconds)

When you have a choice (e.g., a Java application that could use either), prefer Quartz — the explicit ? requirement saves the OR-vs-AND confusion that bites everyone using Unix cron.

Related

Continue reading.