Quartz.NET "?" vs "*"

A cron expression that would be perfectly valid in Hangfire or plain Unix cron throws Support for specifying both a day-of-week AND a day-of-month parameter is not implemented in Quartz.NET, for an expression that doesn't look like it's specifying both of anything. The rule is narrower and stricter than it first appears.

reference guide Quartz.NET

// the actual rule

│

Quartz.NET expressions have both a day-of-month field and a day-of-week field, and the rule is exact: exactly one of the two must be ?. It doesn't matter what the other field contains — *, a specific value, a list, a range, L, # — as long as one field is ?, the expression is valid. The error only fires when neither field is ?.

valid vs invalid
0 0 12 * * ?Valid. Day-of-month is *, day-of-week is ? — fires every day at noon.
0 0 6 ? * MONValid. Day-of-month is ?, day-of-week is MON — fires every Monday at 6 AM.
0 0 12 * * *Invalid. Neither field is ? — throws the "not implemented" error, even though both fields are just wildcards.
0 0 6 15 * MONInvalid, for a different reason: this looks like it's asking for "the 15th, and also every Monday," which cron's AND/OR semantics don't actually resolve the way most people expect even in dialects that allow it. Quartz.NET refuses to guess and requires you to pick one field to leave unconstrained.
The most common trigger isn't a deliberate attempt to combine both fields — it's translating a 5-field Unix or Hangfire expression into Quartz.NET's 6/7-field format by adding a leading seconds field and leaving both day fields as *, the way they were in the original. That translation is invalid; one of the two has to become ?.

// why the restriction exists at all

│

Standard cron resolves day-of-month and day-of-week with OR logic when both are constrained to specific values — "the 15th, OR any Friday" — which is rarely what anyone actually wants and is a well-documented source of schedules that fire far more often than intended. Quartz.NET sidesteps the ambiguity entirely by making it a parse error instead of a silent surprise: you're forced to explicitly say which of the two fields you're not using, via ?, rather than have the parser guess at AND-vs-OR semantics on your behalf.

// build it without guessing

│

The Cron Builder & Explainer generates valid Quartz.NET expressions field-by-field, so the ? placement is handled automatically instead of being something to remember while translating between dialects.

// frequently asked questions

Why does Quartz.NET say day-of-week and day-of-month can't both be set?

Because exactly one of the two fields must be ?. It doesn't matter what the other field contains — *, a specific value, a list, a range — as long as one is ?, the expression is valid. The error only fires when neither field is ?, even if both are just wildcards.

Why does 0 0 12 * * * fail in Quartz.NET when it looks like a valid schedule?

Neither the day-of-month nor day-of-week field is ? — both are *, which Quartz.NET still rejects. Standard cron and Hangfire allow this, but Quartz.NET requires you to explicitly mark one of the two fields as unconstrained with ? rather than leaving both as wildcards.

Why does the ? character exist in Quartz.NET at all?

Standard cron resolves day-of-month and day-of-week with OR logic when both are constrained to specific values — "the 15th, OR any Friday" — which is rarely what anyone actually wants. Quartz.NET sidesteps the ambiguity by making it a parse error instead of a silent surprise, forcing you to say explicitly which field you're not using.

Why does translating a Hangfire cron expression to Quartz.NET break it?

The most common trigger for this error isn't a deliberate attempt to combine both fields — it's adding a leading seconds field to a 5-field Hangfire/Unix expression and leaving both day fields as *, the way they were in the original. That translation is invalid in Quartz.NET; one of the two has to become ?.