Hangfire Cron Job Running on the Wrong Day

The expression parses. It validates. Hangfire's dashboard shows a perfectly reasonable next-run time. And it still fires on the wrong day of the week. This is almost always a day-of-week numbering mismatch — and the fix depends on knowing which scheduler's convention you actually copied the number from.

reference guide Hangfire Cronos Quartz.NET

// three schedulers, three numbering conventions

│

Hangfire has used the Cronos library to parse cron expressions since Hangfire 1.7. Cronos accepts day-of-week values 0 through 7, where both 0 and 7 mean Sunday (the Vixie cron convention) and 1–6 are Monday through Saturday. Quartz.NET uses a different range entirely: 1 through 7, where 1 means Sunday and 7 means Saturday. Traditional Unix cron — what most blog posts and Stack Overflow answers are actually demonstrating — uses 0 through 6, 0 for Sunday.

what each digit means, by scheduler
Hangfire / Cronos0 or 7 = Sunday, 1 = Monday, … 6 = Saturday
Quartz.NET1 = Sunday, 2 = Monday, … 7 = Saturday
Traditional Unix cron0 = Sunday, 1 = Monday, … 6 = Saturday
The digit 1 means Monday in a Hangfire/Cronos expression, but Sunday in a Quartz.NET expression. Copy a "run every Monday" example from a Quartz.NET tutorial straight into RecurringJob.AddOrUpdate and the job will run every Sunday instead — with no error, because 1 is a valid day-of-week value in both systems, just a different day.
the mistake, concretely
a "run every Monday" example copied straight from a Quartz.NET tutorial
RecurringJob.AddOrUpdate("weekly-report", () => SendReport(), "0 6 * * 1");
what actually happens
No exception, no validation warning -- Hangfire's dashboard shows a perfectly valid next run time. The job fires every Sunday, not Monday.
the fix
RecurringJob.AddOrUpdate("weekly-report", () => SendReport(), Cron.Weekly(DayOfWeek.Monday, 6));  // no ambiguous digit to get wrong

// the fix that isn't a cron expression

│

If you're calling RecurringJob.AddOrUpdate with a helper instead of a raw string — Cron.Daily(), Cron.Weekly(DayOfWeek.Monday) — you're using .NET's own System.DayOfWeek enum, not a cron digit at all. DayOfWeek.Sunday is 0 and DayOfWeek.Saturday is 6, which happens to line up with Cronos's 0–6 range exactly. This whole class of bug only exists for hand-written cron strings — the helper methods can't have this problem, because there's no ambiguous digit for the compiler to accept.

side by side
Every Monday at 6 AM (Hangfire, raw cron)0 6 * * 1
Every Monday at 6 AM (Hangfire, helper)Cron.Weekly(DayOfWeek.Monday, 6)
Every Monday at 6 AM (Quartz.NET, raw cron)0 0 6 ? * 2

// how to actually check which one you're looking at

  1. Find the day-of-week field — it's the last field in a 5-field expression, or the last of six in a Quartz.NET expression that starts with a seconds field.
  2. Check whether the same expression has a ? anywhere. Only Quartz.NET uses ?, so its presence confirms the 1=Sunday convention applies.
  3. No ?, and it's a 5-field expression? It's standard cron — either Cronos's 0–7 or traditional 0–6, which only disagree on whether 7 is a second way to write Sunday or invalid. For anything Hangfire actually runs, that's Cronos.
│

When in doubt, don't count fields by hand — paste the expression into the Cron Builder & Explainer and it'll say which day it actually means, for both dialects, before anything gets deployed.

// frequently asked questions

Why does a Hangfire cron job run on the wrong day even though it validates?

Almost always a day-of-week numbering mismatch, not a syntax error. Hangfire's Cronos engine accepts 0–7 where both 0 and 7 mean Sunday. Quartz.NET uses 1–7 where 1 means Sunday. Traditional Unix cron uses 0–6 where 0 means Sunday. Copying a "run every Monday" example from a Quartz.NET tutorial straight into a Hangfire expression makes the job run every Sunday instead, with no error, because the digit is valid in both systems — just a different day.

What does 1 mean in Hangfire's day-of-week field?

Monday. Hangfire's Cronos engine treats 1 through 6 as Monday through Saturday, with 0 or 7 meaning Sunday. This is different from Quartz.NET, where 1 means Sunday.

Does DayOfWeek.Monday in Cron.Weekly() use the same numbering as a raw cron string?

No conflict to worry about — using a helper like Cron.Weekly(DayOfWeek.Monday) uses .NET's own System.DayOfWeek enum, not a cron digit at all. DayOfWeek.Sunday is 0 and DayOfWeek.Saturday is 6, which happens to line up with Cronos's 0–6 range. This class of bug only exists for hand-written cron strings.

How do I tell which cron dialect an expression uses?

Check whether the expression has a ? anywhere — only Quartz.NET uses it, confirming the 1=Sunday convention applies. No ? and it's a 5-field expression means standard cron, and for anything Hangfire actually runs, that's Cronos's 0–7 range. When in doubt, paste it into a tool that states the day explicitly rather than counting fields by hand.