Learn .NET  

๐Ÿ•’ Handling Time Zones in .NET 6: Quartz.NET vs Hangfire for Scheduled Jobs

๐Ÿงพ 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)

FeatureHangfireQuartz.NET
Time Zone SupportโŒ Manualโœ… Native
Ideal ForDelayed jobsScheduled 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 CaseBest 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