Cronos
A fully-featured .NET library for working with Cron expressions. Built with time zones in mind and intuitively handles daylight saving time transitions
Install / Use
/learn @HangfireIO/CronosREADME
Cronos
Cronos is a .NET library for parsing Cron expressions and calculating next occurrences. It was designed with time zones in mind, and intuitively handles Daylight saving time (also known as Summer time) transitions as in *nix Cron.
Please note this library doesn't include any task/job scheduler, it only works with Cron expressions.
- Supports standard Cron format with optional seconds.
- Supports non-standard characters like
L,W,#and their combinations. - Supports schedule jitter via the
Hcharacter, inspired by Jenkins. - Supports reversed ranges, like
23-01(equivalent to23,00,01) orDEC-FEB(equivalent toDEC,JAN,FEB). - Supports time zones, and performs all the date/time conversions for you.
- Does not skip occurrences, when the clock jumps forward to Daylight saving time (known as Summer time).
- Does not skip interval-based occurrences, when the clock jumps backward from Summer time.
- Does not retry non-interval based occurrences, when the clock jumps backward from Summer time.
- Contains 1000+ unit tests to ensure everything is working correctly.
Compatibility
This section explains how Cron expressions should be converted when moving to Cronos.
| Library | Comments |
|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Vixie Cron | When both day-of-month and day-of-week are specified, Cronos uses AND operator for matching (Vixie Cron uses OR operator for backward compatibility). |
| Quartz.NET | Cronos uses different, but more intuitive Daylight saving time handling logic (as in Vixie Cron). Full month names such as september aren't supported. Day-of-week field in Cronos has different values, 0 and 7 stand for Sunday, 1 for Monday, etc. (as in Vixie Cron). Year field is not supported. |
| NCrontab | Compatible |
| CronNET | Compatible |
Installation
Cronos is distributed as a NuGet package, you can install it from the official NuGet Gallery. Please use the following command to install it using the NuGet Package Manager Console window.
PM> Install-Package Cronos
Usage
We've tried to do our best to make Cronos API as simple and predictable in corner cases as possible. So you can only use DateTime with DateTimeKind.Utc specified (for example, DateTime.UtcNow), or DateTimeOffset classes to calculate next occurrences. You cannot use local DateTime objects (such as DateTime.Now), because this may lead to ambiguity during DST transitions, and an exception will be thrown if you attempt to use them.
To calculate the next occurrence, you need to create an instance of the CronExpression class, and call its GetNextOccurrence method. To learn about Cron format, please refer to the next section.
using Cronos;
CronExpression expression = CronExpression.Parse("* * * * *");
DateTime? nextUtc = expression.GetNextOccurrence(DateTime.UtcNow);
The nextUtc will contain the next occurrence in UTC, after the given time, or null value when it is unreachable (for example, Feb 30). If an invalid Cron expression is given, the CronFormatException exception is thrown.
Working with time zones
It is possible to specify a time zone directly; in this case you should pass DateTime with DateTimeKind.Utc flag, or use DateTimeOffset class, since that is smart enough to always point to an exact, non-ambiguous instant.
CronExpression expression = CronExpression.Parse("* * * * *");
TimeZoneInfo easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime? next = expression.GetNextOccurrence(DateTime.UtcNow, easternTimeZone);
DateTimeOffset? next = expression.GetNextOccurrence(DateTimeOffset.UtcNow, easternTimeZone);
If you passed a DateTime object, resulting time will be in UTC. If you used DateTimeOffset, resulting object will contain the correct offset, so don't forget to use it especially during DST transitions (see below).
Working with local time
If you just want to make all the calculations using local time, you'll have to use the DateTimeOffset class, because as I've said earlier, DateTime objects may be ambiguous during Summer time transitions. You can get the resulting local time, using the DateTimeOffset.DateTime property.
CronExpression expression = CronExpression.Parse("* * * * *");
DateTimeOffset? next = expression.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);
var nextLocalTime = next?.DateTime;
Adding seconds to an expression
If you want to specify seconds, use another overload of the Parse method and specify the CronFormat argument as below:
CronExpression expression = CronExpression.Parse("*/30 * * * * *", CronFormat.IncludeSeconds);
DateTime? next = expression.GetNextOccurrence(DateTime.UtcNow);
Getting occurrences within a range
You can also get occurrences within a fixed date/time range using the GetOccurrences method. By default, the from argument will be included when matched, and to argument will be excluded. However, you can configure that behavior.
CronExpression expression = CronExpression.Parse("* * * * *");
IEnumerable<DateTime> occurrences = expression.GetOccurrences(
DateTime.UtcNow,
DateTime.UtcNow.AddYears(1),
fromInclusive: true,
toInclusive: false);
There are different overloads for this method to support DateTimeOffset arguments or time zones.
Cron format
Cron expression is a mask to define fixed times, dates and intervals. The mask consists of second (optional), minute, hour, day-of-month, month and day-of-week fields. All of the fields allow you to specify multiple values, and any given date/time will satisfy the specified Cron expression, if all the fields contain a matching value.
Allowed values Allowed special characters Comment
┌───────────── second (optional) 0-59 * , - / H
│ ┌───────────── minute 0-59 * , - / H
│ │ ┌───────────── hour 0-23 * , - / H
│ │ │ ┌───────────── day of month 1-31 * , - / H L W ?
│ │ │ │ ┌───────────── month 1-12 or JAN-DEC * , - / H
│ │ │ │ │ ┌───────────── day of week 0-6 or SUN-SAT * , - / H # L ? Both 0 and 7 means SUN
│ │ │ │ │ │
* * * * * *
Base characters
In all fields you can use numbers, * to mark a field as every value, and - to specify ranges of values. Reversed ranges like 22-1 (equivalent to 22,23,0,1,2) are also supported.
You can also use H to choose a single value left up to the implementation, for use cases where you might want to distribute load. This is a form a schedule jitter.
It's possible to define steps by combining / with *, H, numbers and ranges. For example, */5 in the minute field describes every 5 minutes and 1-15/3 in day-of-month field describes every 3 days from the 1st to the 15th. Pay attention that */24 is just equivalent to 0,24,48 and */24 in minute field doesn't literally mean every 24 minutes - it means every 0,24,48 minute.
Concatenate values and ranges by ,. Comma works like OR operator. So 3,5-11/3,12 is equivalent to 3,5,8,11,12.
In month and day-of-week fields, you can use names of months or days of weeks abbreviated to first three letters (Jan-Dec or Mon-Sun) instead of their numeric values. Full names like JANUARY or MONDAY aren't supported.
For day of week field, both 0 and 7 stays for Sunday, 1 for Monday.
| Expression | Description | |----------------------|----
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
