How * * * * * works
* * * * * has a star in every field, so it matches every minute of every hour of every day, at second 0. Five-field cron cannot go below one minute. In Azure Functions the first of six fields is seconds, so the same schedule is 0 * * * * *; six stars (* * * * * *) there would run every second.
Variations
| Schedule | Hangfire / crontab | Azure Functions | Quartz.NET |
|---|---|---|---|
| Every minute, 09:00 to 17:59, Monday to Friday | * 9-17 * * MON-FRI | 0 * 9-17 * * MON-FRI | 0 * 9-17 ? * MON-FRI |
| Every minute during one hour (08:00 to 08:59) | * 8 * * * | 0 * 8 * * * | 0 * 8 * * ? |
C# code
Hangfire
// Hangfire: recurring jobs use Cronos. Times are UTC unless you set a time zone.
RecurringJob.AddOrUpdate<ReportJob>(
"daily-report",
job => job.RunAsync(),
"* * * * *",
new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc });
// Cronos on its own
var schedule = CronExpression.Parse("* * * * *");
DateTime? next = schedule.GetNextOccurrence(DateTime.UtcNow);
Azure Functions
// Azure Functions (isolated worker): six fields, seconds first. Times are UTC
// unless WEBSITE_TIME_ZONE is set on the app.
[Function("DailyReport")]
public void Run([TimerTrigger("0 * * * * *")] TimerInfo timer)
{
}
// NCrontab on its own
var schedule = CrontabSchedule.Parse("* * * * *");
DateTime next = schedule.GetNextOccurrence(DateTime.UtcNow);
Quartz.NET
// Quartz.NET with Microsoft.Extensions.DependencyInjection
services.AddQuartz(q =>
{
var jobKey = new JobKey("daily-report");
q.AddJob<ReportJob>(opts => opts.WithIdentity(jobKey));
q.AddTrigger(t => t
.ForJob(jobKey)
.WithIdentity("daily-report-trigger")
.WithCronSchedule("0 * * * * ?", x => x.InTimeZone(TimeZoneInfo.Utc)));
});
Common mistakes
Day numbers that mean different days in Quartz.NET, ranges that wrap in Azure Functions and the time zone each library assumes catch people with every schedule, not only this one. They are listed once, with fixes, on the cron expression builder.
When a run takes longer than the interval
Hangfire creates a new background job at every occurrence, even while the previous one is still running; add [DisableConcurrentExecution] to the job method if runs must not overlap. Quartz.NET also starts a new run on time unless the job class has [DisallowConcurrentExecution]. An Azure Functions timer trigger does not fire again while an invocation is still running, even when the app has scaled out.
FAQ
What is the cron expression for every minute?
* * * * * in Linux crontab, Hangfire (Cronos) and NCrontab; 0 * * * * * in an Azure Functions timer trigger, which has a seconds field; 0 * * * * ? in Quartz.NET.
Does every minute count from when my app starts?
No. Cron follows the clock: * * * * * runs at fixed times (every minute), whenever the app was started or the job registered.
Is there a Hangfire shortcut for it?
Yes: Cron.Minutely() returns "* * * * *" in Hangfire 1.8.
Other common schedules
- Cron every 5 minutes
*/5 * * * * - Cron every 15 minutes
*/15 * * * * - Cron every hour
0 * * * * - Cron expression builder for .NET (any expression, in all three libraries)