[Mini Tutorial] – How to Parse Quartz.Net Cron Expressions

Since I started working at Demac Media I’ve come to realize that the most important aspect of building an eCommerce website is not its front-end design. It’s how the core business needs are addressed through back-end integration. A business might require their data to be transferred to different databases or incorporate special prices for their B2C or B2B customers. All of these business requirements have been incorporated by our integration team in order to help our clients integrate their business properly.

These programs or jobs, as we prefer to call them at Demac, run periodically over the course of a day. We usually define the schedules of these using a cron expression. For example Company A might utilize an ERP system that might update their price and stock quantity. In this case, our integration team would have to develop a UpdatePrice job and a UpdateQuantity job which would obtain data from the ERP system and update the price and quantity in Magento. In the case of the latter jobs, these run every hour or two and update a list of products.

With the number of clients Demac Media has, there are thousands of jobs running each day and it’s hard to predict which jobs will run next. Sometimes customers make changes to their products but are not able to see the changes in Magento right away. We usually try to explain how the job is still due to start, which can be a little vague to predict, based on historical trends, when it will start running.

As mentioned before we use cron expressions or triggers to schedule these jobs. Now the problem with expressions like these is that they look complicated! For example try to guess what the following expression means:

0 0 * * *

As I said it is complicated. This expression would run the job once a day at midnight. You can learn a more about cron expressions on Wikipedia. I would rather not go into too much detail on how they work but essentially the problem is to extract what this expression represents in terms of time.

There are a couple of programs and libraries that help you do that but in our case since we use Quartz.net to schedule our jobs we had to write some code that could help us parse this expression. Although, Quartz.net uses cron expressions which syntactically is the same but they are represented differently – which I realized after hours of Googling and countless hours of hacking around NCrontab. You can check out how their cron expressions work here. Surprisingly, I couldn’t find much code that does that for Quartz.Net cron expressions so I had to develop my own.

Here’s the function I developed that returns a list of dates depending on the cron expression:

ParceCron takes a croneExpression and returns a list of DateTime objects.

public static IEnumerable<DateTime?> ParseCron(string cronExpression) 
{
    if (string.IsNullOrEmpty(cronExpression)) throw new ArgumentException("cronExpression");
    var result = new List<DateTime?>();
    var jobSchedules = new List<DateTime>();
    var quartzHelper = new Quartz.CronExpression(cronExpression);
    var dt = DateTime.Now; 
    var numberOfDays = 0;
    var numberOfSchedules = 30;
    var restrictToDays = 20;

    while (numberOfDays <= restrictToDays)
    {
        var nextScheduledJob = quartzHelper.GetNextValidTimeAfter(dt);

         if (nextScheduledJob != null)
         {
            dt = nextScheduledJob.Value;
            if (result.Count != 0)
            {
               var lastDate = result.Last();
               var diff = dt.Day - lastDate.Value.Day;
               if (diff >=1)
               {
                  numberOfDays++;
                }
            }
           result.Add(nextScheduledJob.GetValueOrDefault().ToLocalTime());
           if (result.Count() == numberOfSchedules)
           {
             break;
           }                                      
        }                
     }
return result;
}

Fortunately, Quartz.net has a function that tells you when a job would run next depending on the cron expression.

var quartzHelper = new Quartz.CronExpression(cronExpression);</pre>

The above line instanciates a new CronExpression class which contains the GetNextValidTimeAfter() method which takes a DateTime object as a parameter.
Initially I set the DateTime object (dt) to the present date and find the occurrence of the next schedule relative to that date. So, the nextScheduledJob variable stores the date on which the scheduled job will run relative to the cron expression and the date we passed.

The loop keeps track of the number of days you want the schedule for. The current function would give you the schedule of the job over the course of 20 days.

This was interesting a project and I am sure there are many ways to solve this problem but this method gels well with the current methods we have in place at Demac.
Until next time, HappyCoding!