Evaluating CRON and RRule expressions in .NET

 
 
  • Gérald Barré

Evaluating recurring date patterns is a common requirement in many applications. Whether you need every day, every Monday at 9 AM, or the last day of each month, you need a reliable way to calculate upcoming occurrences. Meziantou.Framework.Scheduling is a NuGet package that parses and evaluates both CRON expressions and iCalendar recurrence rules (RRule).

#Installation

You can install the package from NuGet:

Shell
dotnet add package Meziantou.Framework.Scheduling

#CRON expressions

CRON is a time-based job scheduling format that originated in Unix systems. The package supports CRON expressions with 5, 6, or 7 fields, covering a wide range of scheduling needs.

##Basic CRON syntax

A CRON expression consists of several fields that define when a job should run:

  • 5 fields: minute hour day-of-month month day-of-week
  • 6 fields: second minute hour day-of-month month day-of-week
  • 7 fields: second minute hour day-of-month month day-of-week year

Each field can contain:

  • A specific value: 5
  • A wildcard: * (any value)
  • A range: 1-5 (values 1 through 5)
  • A list: 1,3,5 (values 1, 3, and 5)
  • A step value: */15 (every 15 units)

##Using CRON expressions

Here's how to parse and use CRON expressions:

C#
using Meziantou.Framework.Scheduling;

// Parse a CRON expression
var cron = CronExpression.Parse("0 9 * * MON-FRI");

// Get the next occurrences
var startDate = DateTime.Now;
var nextOccurrences = cron.GetNextOccurrences(startDate).Take(10);

foreach (var occurrence in nextOccurrences)
{
    Console.WriteLine(occurrence);
}

##Predefined expressions

The package also supports predefined CRON expressions for common schedules:

C#
// @yearly or @annually - Run once a year at midnight on January 1
CronExpression.Parse("@yearly");

// @monthly - Run once a month at midnight on the first day
CronExpression.Parse("@monthly");

// @weekly - Run once a week at midnight on Sunday
CronExpression.Parse("@weekly");

// @daily or @midnight - Run once a day at midnight
CronExpression.Parse("@daily");

// @hourly - Run once an hour at the beginning of the hour
CronExpression.Parse("@hourly");

##Advanced CRON features

The package supports several advanced CRON features:

Last day of month (L):

C#
// Last day of every month
CronExpression.Parse("0 0 L * *");

Nearest weekday (W):

C#
// Nearest weekday to the 15th of the month
CronExpression.Parse("0 0 15W * *");

// Last weekday of the month
CronExpression.Parse("0 0 LW * *");

Nth occurrence (#):

C#
// First Monday of every month (1-based index)
CronExpression.Parse("0 0 * * MON#1");

// Second Tuesday of every month
CronExpression.Parse("0 0 * * TUE#2");

// Third Friday of every month
CronExpression.Parse("0 0 * * FRI#3");

#iCalendar recurrence rules (RRule)

The package also supports iCalendar recurrence rules as defined in RFC 5545 and RFC 2445. This format is used by calendar applications like Google Calendar, Outlook, and Apple Calendar.

##Using RRule

C#
using Meziantou.Framework.Scheduling;

// Parse an RRule
var rrule = "FREQ=DAILY;UNTIL=20250131T140000Z;BYMONTH=1";
if (RecurrenceRule.TryParse(rrule, out var rule, out var error))
{
    var nextOccurrences = rule.GetNextOccurrences(DateTime.Now).Take(50);

    foreach (var occurrence in nextOccurrences)
    {
        Console.WriteLine(occurrence);
    }
}
else
{
    Console.WriteLine($"Error: {error}");
}

##RRule examples

Here are some common recurrence patterns:

C#
// Every day
RecurrenceRule.Parse("FREQ=DAILY");

// Every 2 days, 10 times
RecurrenceRule.Parse("FREQ=DAILY;INTERVAL=2;COUNT=10");

// Every Monday
RecurrenceRule.Parse("FREQ=WEEKLY;BYDAY=MO");

// Every weekday (Monday to Friday)
RecurrenceRule.Parse("FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR");

// Monthly on the 1st and 15th
RecurrenceRule.Parse("FREQ=MONTHLY;BYMONTHDAY=1,15");

// Yearly on January 1st until 2030
RecurrenceRule.Parse("FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1;UNTIL=20300101");

#Comparison: CRON vs RRule

Both formats have their strengths:

CRON expressions are:

  • Concise and widely recognized
  • Great for simple schedules
  • Commonly used in Unix/Linux environments
  • Limited in expressing complex patterns

iCalendar RRule is:

  • More expressive and flexible
  • Standardized by RFC 5545
  • Used by calendar applications
  • Better for complex recurring patterns
  • Self-documenting with named parameters

Choose CRON expressions for simple, Unix-like schedules. Choose RRule when you need maximum flexibility or interoperability with calendar applications.

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?