2

Using Quartz.net Server version 2.1.2 which I upgraded to from version 2.0 because of a lack of support for UTC timezone offsets. Jobs are not being sent at the time we specify, and it is seemingly because of timezone offsets.

I have three job types:

Job Frequency Requirments

  1. Daily (once a day at a given time)
  2. Weekly (on one to N days, once each day at a given time)
  3. Monthly (on one to n days, once a day, or at the last day)

For all three I use Cron expressions with SimpleTriggers.

My question is: what are the things I need to tick off to verify that the jobs I am scheduling, and the jobs that are going to run on the server, will run on time in their timezone?

It appears that simply specifying the timezone is not enough; I specify the timezone like this

  dailyTrigger.TimeZone = BrainTimeZone;

but on the server that holds the quartz instance (in PST) gets a job scheduled for 2:00pm in NYC (EST), the "Next Run Time" should be 2:00pm, but it shows and runs at noon.

Here are a few excellent s/o articles on Quartz Time Zones:

Quartz.net UTC Resources:

Here is how I currently schedule a daily:

    private void CreateDaily()
    {
        var expression = CronScheduleBuilder
                             .DailyAtHourAndMinute(GetNormalizedHour(), Minute)
                             .InTimeZone(TimeZone)
                             .Build() as CronTriggerImpl;

        IJobExecutionContext jobContext = GetJobContext();


        IJobDetail job = JobBuilder.Create<MailJob>()
                                   .WithIdentity(JobName, GroupName)
                                   .UsingJobData(jobContext.JobDetail.JobDataMap)
                                   .Build();
         ScheduleBuilder<>cronExpressionString

        var cronExpressionString = expression.CronExpressionString; // returns a cron expr.
        var dailyTrigger = (ICronTrigger)TriggerBuilder.Create()
                                                       .WithIdentity(JobName, GroupName)
                                                       .WithSchedule(cronExpressionString)
                                                       .Build();
        dailyTrigger.TimeZone = BrainTimeZone;

        this.JobTrigger = dailyTrigger;
        this.JobDetail = job;
        this.Success = true;
    }

I tried to use a DailyTimeIntervalTriggerImpl, but this does not seem like the right trigger for any of those 3 above interval types.

    private void DailyJob()
    {

        #region Duration

        var daysOfWeek = new Quartz.Collection.HashSet<System.DayOfWeek>
            {
                System.DayOfWeek.Monday,
                System.DayOfWeek.Tuesday,
                System.DayOfWeek.Wednesday,
                System.DayOfWeek.Thursday,
                System.DayOfWeek.Friday,
                System.DayOfWeek.Saturday,
                System.DayOfWeek.Sunday,
            };

        DateTimeOffset startTime = DateTime.Now;
        DateTimeOffset endTime = DateTime.Now.AddYears(1);

        TimeOfDay startTimeOfDay = TimeOfDay.HourMinuteAndSecondOfDay(Hour, Minute, 0);
        TimeOfDay endTimeOfDay = TimeOfDay.HourMinuteAndSecondOfDay(Hour, Minute, 30);

        #endregion

        IJobExecutionContext jobContext = GetJobContext();

        if (JobName == null || GroupName == null) {
            this.Success = false;
            return;
        }

        var dailyTrigger = new DailyTimeIntervalTriggerImpl
        {
            StartTimeUtc = startTime.ToUniversalTime(),
            EndTimeUtc = endTime.ToUniversalTime(),
            StartTimeOfDay = startTimeOfDay,
            EndTimeOfDay = endTimeOfDay,
            RepeatIntervalUnit = IntervalUnit.Week,
            DaysOfWeek = daysOfWeek,
            // RepeatInterval = 1,
            TimeZone = TimeZone,
            Key = new TriggerKey(JobName, GroupName),
        };     

        // Compute fire times just to show simulated fire times
        IList<DateTimeOffset> fireTimes = TriggerUtils.ComputeFireTimes(dailyTrigger, null, 1000);

        foreach (var dateTimeOffset in fireTimes)
        {
            QuartzLog("Daily trigger has been computed - fireTimes as follows:\r\n\r\n");
            QuartzLog(string.Format("utc:{0} - adjusted time:{1} - timezone:{2}", dateTimeOffset,
              TimeZoneInfo.ConvertTimeFromUtc(dateTimeOffset.DateTime, BrainTimeZone), BrainTimeZone.DisplayName));
        }

I am in the process of refactoring all my quartz layers, so I am looking for best practices and a bullet proof method of assuring that jobs will run at the times we specify, regardless of where the Quartz.net job server is located, and where the user's time zone originated from and where they want it to end up arriving at.

We are moving our quartz.net servers to AWS so we will have a distributed server farm to host these with timezones that can change.

How can I set up my quartz architecture so that it is dynamic enough and as I stated above, bullet proof, in sending jobs on time - regardless of TimeZones / Offsets / DaylightSavings / LeapYear / Sleat / Snow / Rain / Zombie Attacks / Y2K / Meteoric ELE / etc. ?

Thank you.

Community
  • 1
  • 1
Shawn J. Molloy
  • 2,457
  • 5
  • 41
  • 59

0 Answers0