Bodu.Globalization.Recurrence
1.3.0
dotnet add package Bodu.Globalization.Recurrence --version 1.3.0
NuGet\Install-Package Bodu.Globalization.Recurrence -Version 1.3.0
<PackageReference Include="Bodu.Globalization.Recurrence" Version="1.3.0" />
<PackageVersion Include="Bodu.Globalization.Recurrence" Version="1.3.0" />
<PackageReference Include="Bodu.Globalization.Recurrence" />
paket add Bodu.Globalization.Recurrence --version 1.3.0
#r "nuget: Bodu.Globalization.Recurrence, 1.3.0"
#:package Bodu.Globalization.Recurrence@1.3.0
#addin nuget:?package=Bodu.Globalization.Recurrence&version=1.3.0
#tool nuget:?package=Bodu.Globalization.Recurrence&version=1.3.0
Bodu.Globalization.Recurrence
API stability - Stable. The public API surface is committed: breaking changes are reserved for a major-version bump per SemVer, except a deliberate break that is recorded in the release notes and in the package's
CompatibilitySuppressions.xml. See versioning and support.
Recurrence-rule evaluation for .NET: four ways to express "when does this happen again?", each
answering next- and previous-occurrence queries over DateTime and DateTimeOffset.
using Bodu.Globalization.Recurrence;
// RFC 5545 RRULE - the second Tuesday of every month
var rule = RecurrenceRule.Parse("FREQ=MONTHLY;BYDAY=2TU", null);
DateTime? next = rule.GetNextOccurrence(start: new DateTime(2026, 1, 1),
after: new DateTime(2026, 3, 5),
inclusive: false);
// Vixie cron - 06:30 on weekdays
var cron = CronExpression.Parse("30 6 * * 1-5");
// "every 4 hours after the last completed run"
var every4h = AnchoredInterval.Parse("PT4H", null);
Installation
dotnet add package Bodu.Globalization.Recurrence
Targets net8.0 and net10.0. Depends only on
Bodu.Core - not on
Bodu.Globalization.Calendar, which it is a sibling of rather than a layer on.
The four forms
| Type | Grammar | For |
|---|---|---|
RecurrenceRule |
RFC 5545 RRULE |
Calendar-style recurrence at every frequency from SECONDLY to YEARLY: FREQ/INTERVAL/COUNT/UNTIL/WKST and the full BY* model incl. BYSETPOS |
RecurrenceSet |
iCalendar property block | One or more rules composed with explicit RDATE additions and EXDATE exclusions |
CronExpression |
Vixie cron | Five-field, optional-seconds six-field, the @yearly…@hourly macros, and the Quartz day tokens L, W, # and ? |
AnchoredInterval |
RFC 5545 §3.3.6 duration | anchor + k·interval for k ≥ 1, with the anchor passed per query |
RecurrenceRule implements IParsable/ISpanParsable/IFormattable and round-trips its canonical
text. Every form carries a defect-naming TryParse(s, out result, out failureMessage) overload that
says what was wrong rather than just returning false, and compares by value.
Purity
Every answer is a pure function of its arguments - no wall clock, no machine time zone. A query
takes the instant to search from, so the same inputs always give the same answer, which makes
schedules testable without freezing time. This is enforced, not merely intended: a metadata-scan
guard (PurityTests) fails the build if a banned wall-clock or local-time-zone API appears anywhere
in the assembly.
Correctness
Validated against an RFC 5545 §3.8.5.3 worked-example corpus, then hardened against defects reported
to other implementations - python-dateutil, rrule.js, ical4j, ical.net, ical.js, libical, lib-recur,
ice_cube, Cronos, NCrontab, cronie, croniter, robfig/cron and Quartz - with that corpus kept as
regression tests. Fixes it produced include Vixie's leading-character rule for cron day-field
restriction, candidate-set deduplication before BYSETPOS/COUNT, the BYDAY ordinal when BYDAY
limits alongside BYMONTHDAY, and BYWEEKNO numbering generalized from the ISO rule to WKST.
The cron engine is also held to the test suites of 21 cron libraries, restated in its dialect, and to
the scenarios of the fixes in their release notes: 5,723 runnable rows. They found two defects, both
fixed: a search near either end of the calendar threw rather than answering null, and a search gave
up after twelve years, though some expressions match only decades apart.
Known limits
- The Quartz day tokens keep Vixie's weekday numbering (0 or 7 for Sunday) and union rule, so a Quartz expression that names weekdays by number, or restricts both day fields, reads differently.
- A
CronExpressionsearch for an expression that never matches scans a whole 400-year calendar cycle before answeringnull. - No
.icsreader - this package evaluates rules, it does not parse calendar files.
Part of the Bodu utility library.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.