๐งพ Scenario: Daily Invoice Email at 9 AM Local Time
Requirements:
Users are in India, UK, and US
Email should be sent at 9 AM local time
Server runs in UTC
App is built in .NET 6 Web API
๐ง Quartz.NET vs Hangfire (Quick Comparison)
Feature | Hangfire | Quartz.NET |
---|
Time Zone Support | โ Manual | โ
Native |
Ideal For | Delayed jobs | Scheduled jobs |
Dashboard | โ
Yes | โ No |
Retry/Error Handling | โ
Yes | โ ๏ธ Manual |
Distributed Setup | โ
Yes | โ
Yes |
๐ ๏ธ Option 1: Quartz.NET for Time-Sensitive Jobs
โ
Best for scheduled tasks at specific times in specific time zones
๐ฆ Job Class
public class InvoiceJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine($"Invoice job running at {DateTime.UtcNow}");
// Custom logic: fetch users in IST and send emails
return Task.CompletedTask;
}
}
๐๏ธ Register Job in Program.cs (or a Hosted Service)
IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
await scheduler.Start();
IJobDetail job = JobBuilder.Create<InvoiceJob>()
.WithIdentity("invoiceJob", "billing")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("dailyTrigger", "billing")
.WithCronSchedule("0 0 9 ? * *", x => x
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("India Standard Time")))
.Build();
await scheduler.ScheduleJob(job, trigger);
โ
This job runs daily at 9 AM IST, no matter where your server is hosted.
๐ ๏ธ Option 2: Hangfire for Delayed or Event-Based Jobs
โ
Best for fire-and-forget or delayed jobs
๐ฆ Delayed Job Example
BackgroundJob.Schedule(() => SendEmail(userId), TimeSpan.FromMinutes(10));
๐ฆ Recurring Job Example
RecurringJob.AddOrUpdate(
"daily-invoice",
() => SendDailyInvoices(),
Cron.Daily
);
โ ๏ธ Time Zone Limitation: Hangfire schedules in server time.
To support multiple time zones:
Store user time zones in the database
Convert local time to UTC when scheduling
Handle logic in the job execution if needed
๐๏ธ Run Jobs on a Separate Server
Both Quartz.NET and Hangfire can run in a dedicated .NET 6 Worker Service:
+-------------------+ +-------------------------+
| Web API | --> | .NET 6 Worker (Jobs) |
+-------------------+ +-------------------------+
Database Job Scheduling Engine
โ
This setup improves scalability, separation of concerns, and distributed job processing.
๐ฏ Which Should You Use?
Use Case | Best Choice |
---|
Time-specific jobs in user time zones | โ
Quartz.NET |
Delayed jobs or event-driven triggers | โ
Hangfire |
Need dashboard, retries, failure tracking | โ
Hangfire |
Hybrid needs (scheduled + triggered) | โ
Use both |
โ
Best Practices
Store all timestamps in UTC
Convert to local time in the UI
Avoid relying on server's local time
Use Quartz.NET for precision, Hangfire for flexibility