Philiprehberger.CronExpression

Parse, validate, and evaluate cron expressions with next/previous occurrence calculation, shortcut aliases, fluent builder, human-readable descriptions, timezone-aware scheduling, exclusion calendars, and Nth weekday support.
Installation
dotnet add package Philiprehberger.CronExpression
Usage
using Philiprehberger.CronExpression;
// Parse a cron expression
var schedule = Cron.Parse("*/15 9-17 * * MON-FRI");
// Get the next occurrence
DateTimeOffset next = schedule.NextOccurrence(DateTimeOffset.UtcNow);
// Get the previous occurrence
DateTimeOffset prev = schedule.PreviousOccurrence(DateTimeOffset.UtcNow);
Shortcut Aliases
var daily = Cron.Parse("@daily"); // 0 0 * * *
var hourly = Cron.Parse("@hourly"); // 0 * * * *
var weekly = Cron.Parse("@weekly"); // 0 0 * * 0
var monthly = Cron.Parse("@monthly"); // 0 0 1 * *
var yearly = Cron.Parse("@yearly"); // 0 0 1 1 *
Seconds Field
// 6-field format: second minute hour day-of-month month day-of-week
var schedule = Cron.Parse("*/30 * * * * *"); // Every 30 seconds
var next = schedule.NextOccurrence(DateTimeOffset.UtcNow);
Fluent Builder
// Build expressions programmatically
var schedule = new CronBuilder()
.At(9, 30)
.OnDaysOfWeek(DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday)
.Build();
// Step expressions
var every5Min = new CronBuilder().Every(5).Minutes().Build();
var every2Hr = new CronBuilder().Every(2).Hours().Build();
Human-Readable Descriptions
var schedule = Cron.Parse("*/5 * * * *");
Console.WriteLine(schedule.Describe()); // "Every 5 minutes"
var weekday = Cron.Parse("0 9 * * 1-5");
Console.WriteLine(weekday.Describe()); // "At 09:00 on Monday through Friday"
var yearly = Cron.Parse("0 0 1 1 *");
Console.WriteLine(yearly.Describe()); // "At 00:00 on January 1st"
Quick Matching
// Check if a time matches a cron expression
bool matches = Cron.IsMatch("0 9 * * *", DateTimeOffset.UtcNow);
Listing Occurrences
var schedule = Cron.Parse("0 0 1 * *"); // First of every month
var start = new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
var end = new DateTimeOffset(2026, 12, 31, 23, 59, 59, TimeSpan.Zero);
foreach (var occurrence in schedule.GetOccurrences(start, end))
{
Console.WriteLine(occurrence);
}
Timezone-Aware Scheduling
// Evaluate the schedule in a specific time zone (handles DST correctly)
var schedule = Cron.Parse("0 9 * * *"); // Every day at 9:00 AM
var eastern = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
DateTimeOffset next = schedule.NextOccurrence(DateTimeOffset.UtcNow, eastern);
// List occurrences in a time zone
var occurrences = schedule.GetOccurrences(start, end, eastern);
Exclusion Calendar
// Skip specific dates (holidays, maintenance windows, etc.)
var calendar = new ExclusionCalendar(new[]
{
new DateOnly(2026, 12, 25), // Christmas
new DateOnly(2026, 1, 1), // New Year's Day
});
var schedule = Cron.Parse("0 9 * * MON-FRI");
DateTimeOffset next = schedule.NextOccurrence(DateTimeOffset.UtcNow, calendar);
// Combine with timezone
var occurrences = schedule.GetOccurrences(start, end, eastern, calendar);
Nth Weekday
// Third Friday of every month at 9:00 AM
var schedule = Cron.Parse("0 9 * * 5#3");
DateTimeOffset next = schedule.NextOccurrence(DateTimeOffset.UtcNow);
// Second Monday at midnight
var schedule2 = Cron.Parse("0 0 * * 1#2");
Safe Parsing
if (Cron.TryParse("*/5 * * * *", out var schedule))
{
Console.WriteLine(schedule.NextOccurrence(DateTimeOffset.UtcNow));
}
API
Cron
| Method | Description |
|---|
Parse(string) | Parse a cron expression (5-field, 6-field, or alias) |
TryParse(string, out CronSchedule) | Try to parse without throwing |
IsMatch(string, DateTimeOffset) | Check if a time matches an expression |
CronSchedule
| Method | Description |
|---|
IsMatch(DateTimeOffset) | Check if a time matches this schedule |
NextOccurrence(DateTimeOffset) | Get the next matching time after the given time |
NextOccurrence(DateTimeOffset, ExclusionCalendar?) | Get the next matching time, skipping excluded dates |
NextOccurrence(DateTimeOffset, TimeZoneInfo) | Get the next matching time in a time zone |
NextOccurrence(DateTimeOffset, TimeZoneInfo, ExclusionCalendar?) | Get the next matching time in a time zone, skipping excluded dates |
PreviousOccurrence(DateTimeOffset) | Get the previous matching time before the given time |
GetOccurrences(DateTimeOffset, DateTimeOffset) | List all matching times in a range |
GetOccurrences(DateTimeOffset, DateTimeOffset, ExclusionCalendar?) | List matching times in a range, skipping excluded dates |
GetOccurrences(DateTimeOffset, DateTimeOffset, TimeZoneInfo) | List matching times in a range in a time zone |
GetOccurrences(DateTimeOffset, DateTimeOffset, TimeZoneInfo, ExclusionCalendar?) | List matching times in a range in a time zone, skipping excluded dates |
Describe() | Get a human-readable description of the schedule |
HasSeconds | Whether the schedule uses 6-field format with seconds |
ExclusionCalendar
| Method | Description |
|---|
Add(DateOnly) | Add a blackout date |
AddRange(IEnumerable<DateOnly>) | Add multiple blackout dates |
Remove(DateOnly) | Remove a blackout date |
IsExcluded(DateOnly) | Check if a date is excluded |
IsExcluded(DateTimeOffset) | Check if a time falls on an excluded date |
Clear() | Remove all excluded dates |
GetDates() | Get all excluded dates in ascending order |
Count | Number of excluded dates |
CronBuilder
| Method | Description |
|---|
EveryMinute() | Set minute field to wildcard |
EveryHour() | Set minute to 0 |
AtMinute(int) | Set a specific minute |
AtHour(int) | Set a specific hour |
At(int, int) | Set hour and minute |
OnDaysOfWeek(params DayOfWeek[]) | Set specific days of week |
OnDaysOfMonth(params int[]) | Set specific days of month |
OnMonths(params int[]) | Set specific months |
Every(int).Minutes() | Set step interval on minutes |
Every(int).Hours() | Set step interval on hours |
Build() | Build and return a CronSchedule |
Supported Aliases
| Alias | Equivalent |
|---|
@yearly / @annually | 0 0 1 1 * |
@monthly | 0 0 1 * * |
@weekly | 0 0 * * 0 |
@daily / @midnight | 0 0 * * * |
@hourly | 0 * * * * |
Development
dotnet build src/Philiprehberger.CronExpression.csproj --configuration Release
Support
If you find this project useful:
⭐ Star the repo
🐛 Report issues
💡 Suggest features
❤️ Sponsor development
🌐 All Open Source Projects
💻 GitHub Profile
🔗 LinkedIn Profile
License
MIT