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 cron | Quartz | |
|---|---|---|
| Fields | 5 | 6 (year optional, → 7) |
| Seconds field | No | Yes (leftmost) |
| Year field | No | Optional |
| Special chars | * , - / | * , - / ? L W # |
| Day-of-week start | 0=Sunday | 1=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.
| Unix | Quartz equivalent |
|---|---|
0 9 * * 1-5 | 0 0 9 ? * MON-FRI |
*/5 * * * * | 0 0/5 * * * ? |
0 0 1 * * | 0 0 0 1 * ? |
0 22 * * 5 | 0 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): use28-31in day-of-month, then check[[ $(date -d tomorrow +\%d) == 01 ]]in your script15W: use14,15,16in day-of-month and gate on weekday inside the script2#1(first Monday): use1-7in day-of-month and1in 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, anacron | Unix cron |
Spring @Scheduled, Quartz Scheduler | Quartz (or Spring's variant) |
| AWS EventBridge | Quartz-like with year |
| GitHub Actions, Kubernetes CronJob | Unix cron |
| Azure Functions Timer | NCRONTAB (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.