Cron field count: a six-field expression still installs, and runs wrong

crontab reads five time fields and treats everything after them as the command. Paste a six-field Spring expression and it installs without complaint — noon every day becomes midnight on the 12th of each month, and a five-minute job drops from 287 runs a day to 5.

Schedules below were computed from each expression rather than guessed, starting at 2026-10-17 09:00 local time.

What the measurement shows

Three expressions, each copied from Quartz or Spring documentation, read twice: once as the six-field format they were written for, and once the way crontab reads them.

0 0 12 * * ?        Spring: noon every day
  as written        2026-10-17 12:00   2026-10-18 12:00   2026-10-19 12:00    1 / day
  as crontab        2026-11-12 00:00   2026-12-12 00:00   2027-01-12 00:00    0 / day
                    schedule "0 0 12 * *", command "?"

0 0/5 * * * ?       Quartz: every five minutes
  as written        2026-10-17 09:05   2026-10-17 09:10   2026-10-17 09:15  287 / day
  as crontab        2026-10-17 10:00   2026-10-17 15:00   2026-10-17 20:00    5 / day
                    schedule "0 0/5 * * *", command "?"

0 15 10 * * ?       Quartz: 10:15 every day
  as written        2026-10-17 10:15   2026-10-18 10:15   2026-10-19 10:15    1 / day
  as crontab        2026-11-10 15:00   2026-12-10 15:00   2027-01-10 15:00    0 / day
                    schedule "0 15 10 * *", command "?"

Every field shifts one place to the left. Seconds land in minutes, minutes in hours, and the hour becomes a day of the month — which is why a daily job turns into a monthly one. The middle case is the nastiest in production: it keeps running, so nothing looks broken, at a fifty-seventh of the intended frequency.

Why nothing rejects it

There is no sixth field to be invalid. The system manual is explicit about the layout:

field          allowed values
-----          --------------
minute         0-59
hour           0-23
day of month   1-31
month          1-12 (or names, see below)
day of week    0-7 (0 or 7 is Sun, or use names)

                                           — man 5 crontab

Five time fields, “followed by a command”. The parser takes five whitespace-separated tokens and stops; whatever remains on the line is the command string. So 0 0 12 * * ? is a perfectly well-formed entry: the schedule 0 0 12 * * running the command ?. Both halves obey the format. The entry installs, cron accepts it, and nothing anywhere reports a problem.

The command is the other half of the failure

That leftover ? becomes the program cron tries to run. It does not exist, the shell fails, and cron mails the error to the crontab owner. On a machine with no local mail delivery — which is most of them now — that message goes nowhere.

So the failure is quiet twice over. The schedule is wrong, and the evidence that anything ran is discarded. A job that was meant to run every five minutes instead fails five times a day into a mail spool nobody reads.

Five fields, six fields, and which is which

crontab, Jenkins, Kubernetes CronJob     5    min hour dom mon dow
Quartz, Spring @Scheduled                6    sec min hour dom mon dow
Quartz with year                         7    sec min hour dom mon dow year
AWS EventBridge                          6    min hour dom mon dow year

The layouts overlap in the middle, which is exactly what makes this survive review. Read the first token of an unfamiliar expression: in a six-field Quartz schedule it is almost always 0, because jobs are rarely scheduled at a particular second. A leading 0 followed by five more tokens is the signature.

The other reliable tell is ?. It is a Quartz convention meaning “unspecified” in one of the two day fields, and crontab has no equivalent. If an expression contains one, it did not come from a crontab.

Converting one to the other

Dropping the seconds field gets you most of the way, but the remaining five are not identical either.

Quartz  0 15 10 * * ?
          drop seconds        15 10 * * ?
          ? becomes *         15 10 * * *
crontab                       15 10 * * *      10:15 every day

Three more differences bite after that. Quartz numbers days of the week from 1 for Sunday, while crontab uses 0 through 7 with both ends meaning Sunday, so any numeric day-of-week needs shifting. Quartz accepts L, W and # for last day, nearest weekday and nth weekday of the month; crontab understands none of them. And if the seconds field was anything but 0, the schedule cannot be expressed in crontab at all — its finest resolution is one minute.

Paste an expression into the parser on this site to see the field count it was read with and the next five run times, which is the quickest way to catch a schedule that shifted. It runs in the page, so an expression from a production crontab never leaves your machine.

Frequently asked questions

How many fields does a cron expression have?

It depends on whose cron, which is the whole problem. The crontab format documented in the system manual takes five time fields — minute, hour, day of month, month, day of week — and treats everything after the fifth as the command to run. Quartz and Spring use six, putting seconds in front, and Quartz allows a seventh for the year. Jenkins uses five. Kubernetes CronJob uses five. AWS EventBridge uses six but puts the year last rather than seconds first. Because the field layouts overlap in the middle, an expression written for one system is usually still syntactically valid in another, so nothing complains. The schedule simply means something else.

Why does crontab not reject a six-field expression?

Because there is no sixth field to reject. The manual states that commands are executed when the minute, hour and month of year fields match and at least one of the two day fields matches, and that the time and date fields are followed by a command. The parser takes five whitespace-separated tokens and stops; the remainder of the line is the command string, whatever it happens to be. So 0 0 12 * * ? parses cleanly as a schedule of 0 0 12 * * with the command ?. Both halves are well formed by the rules of the format. The entry installs, cron accepts it, and the only sign that anything is wrong is that the schedule is not the one you wanted.

What actually happens to the command?

It becomes whatever tokens were left over, which for Quartz-style expressions is usually a single question mark. The shell tries to run a program named ?, fails, and cron mails the error to the crontab owner — on a machine with no mail delivery configured, which is most of them now, that output goes nowhere. So the failure is doubly quiet: the schedule is wrong, and the evidence that it ran at all is discarded. If you paste an expression that ends in a year instead, the leftover token is a number, and you get the same shape of failure with a different missing command name. Either way the entry sits in the crontab looking correct, because the part a human reads — the schedule — is the part that still parses. The only reliable check is whether the job's side effects actually happened.

How wrong does the schedule get?

Far enough that it changes the kind of job it is. We computed the next runs for three expressions taken from Quartz and Spring documentation. 0 0 12 * * ? means noon every day in Spring; read as crontab it is 0 0 12 * *, which is midnight on the twelfth of each month — monthly instead of daily, and at the wrong hour. 0 15 10 * * ? means 10:15 every day; as crontab it becomes 15:00 on the tenth of each month. The arithmetic shifts every field left by one, so seconds become minutes, minutes become hours, and the hour becomes a day of the month.

How do I convert a Quartz expression to crontab?

Drop the leading seconds field, then translate what is left, because the remaining five are not quite the same either. Quartz uses ? in the day fields to mean unspecified, which crontab has no concept of — replace it with an asterisk. Quartz numbers days of the week from 1 for Sunday while crontab uses 0 through 7 with both 0 and 7 meaning Sunday, so a numeric day-of-week needs adjusting. Quartz also accepts L, W and # for last, nearest weekday and nth weekday, none of which crontab understands. And if the seconds field was anything other than 0, crontab cannot express the schedule at all, because its finest resolution is one minute.

References

For the other arithmetic that makes a cron schedule mean something other than it reads, see why */45 is not every 45 minutes and why the two day fields combine with OR. More tools at withuse.io/tools.