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 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.