Bodu.Globalization.Calendar Namespace
- Packages
Purpose
Bodu.Globalization.Calendar resolves culturally and algorithmically significant dates - public holidays, observances, religious festivals, and recurring notable dates - from an authored rule document into concrete occurrences for a requested year, date, or range and territory.
A rule describes the what and how of a notable date - a fixed calendar date, an nth-weekday-of-month recurrence, a weekday near a fixed date, a fixed offset from another rule, or a named algorithm (Gregorian / Orthodox Easter, solar equinoxes, lunar phases, lunisolar festivals). Rules are authored on the notable-date schema (urn:bodu:globalization:calendar) as XML or JSON, loaded eagerly into an immutable NotableDateResource, and resolved through NotableDateService.
Reach for this library when a DateTime.DayOfWeek check is not enough: when you need Easter Sunday in year N, when a fixed holiday that lands on a weekend rolls to a substitute weekday, or when you need a culture-aware set of notable dates for a territory and year - optionally extended by external plugin assemblies under a deny-by-default trust policy.
Static documentation
- Bodu.Globalization.Calendar introduction - package family, mental model, headline types, scenarios.
- Bodu.Globalization.Calendar getting started - install and minimal samples for loading a resource, resolving dates, and working-day arithmetic.
- Bodu.Globalization.Calendar guides -
NotableDateService, rule authoring, date-calculation algorithms, companion data packs, dependency injection, caching notable dates, plugin trust, validation diagnostics, binary rule packs, builder round-trip guarantees.
Companion packages
Bodu.Globalization.Calendar.Builder- a fluent C# API for authoring notable-date documents in code, with XML / JSON serialization and load/save.Bodu.Globalization.Calendar.DependencyInjection-Microsoft.Extensions.DependencyInjectionintegration:services.AddNotableDateService(...)/AddReloadableNotableDateService(...)registerINotableDateServiceas a singleton over a loadedNotableDateResource.Bodu.Globalization.Calendar.Plugins- trust-gated loading of external assemblies that contribute custom INotableDateAlgorithm implementations.Bodu.Globalization.Calendar.<Region>- curated public-holiday resources for theAmericas,AsiaPacific,Europe,MiddleEast, andAfricaterritory bundles.
Key types
Entry points and results
- NotableDateService - the main resolver (and the primary INotableDateService implementation). Built over an immutable NotableDateResource, optionally composed with a custom algorithm registry, collision resolver, adjustment handler / trigger registries, and code-first providers.
- ReloadableNotableDateService - an
INotableDateServicebuilt over an INotableDateResourceProvider that rebuilds itself when the provider's resource reference is swapped. - NotableDateServiceOptions - the options object that bundles the optional collaborators (algorithm registry, collision resolver, adjustment registries, code-first providers) when constructing a service.
- NotableDate - the materialized result record: the emitted
Date(the observed date when an adjustment applied), the originally calculatedActualDate,IsObserved, the ruleIdentity,DisplayName,TerritoryCode, NotableDateCategory,Priority,DurationDays/EndDate,IsNonWorkingDay,Tags, and theAdjustmentPolicyId/AdjustmentReasonaudit pair. - NotableDateFilter - a composable predicate over resolved occurrences, built via static factory methods (
ForCategory,ForAnyCategory,WithName,WithId,WithTag,WithAnyTag,WithAllTags,WithMinDuration,IsNonWorkingDay,WasAdjusted,InDateRange) and combined withAnd,Or,Not,AllOf,AnyOf. - NotableDateSequenceExtensions - sequence transforms over resolved occurrences:
WithActualOccurrences()expands observed-only results into the full actual + observed timeline (the consumer-side equivalent of anActualAndObservedemission), preserving the standardResolveordering. - NotableDateCategory - categorization:
PublicHoliday,BankHoliday,Observance,Remembrance,Cultural,Religious,Seasonal,Civic,School,Regional,Other,None. - TerritoryCode - a strongly-typed ISO 3166 country / subdivision code with parent/child containment (
AU-NSWis contained byAU). Implicitly converts to thestringterritory argument the service accepts. - DateRange, CalendarSystem - the inclusive
[StartDate, EndDate]query range and the calendar a rule's strategy is expressed in (Gregorian,Hijri,UmmAlQura,Hebrew,Persian,ChineseLunisolar).
Resources and rule model
- NotableDateResourceLoader - the entry point that parses XML / JSON (string or
Stream), resolves<Imports>, applies ID-targeted overrides, and returns a validated NotableDateResource; throws NotableDateValidationException carrying the NotableDateValidationDiagnostic list on any error-severity diagnostic. - NotableDateResource, NotableDateDefinition, NotableDateRule, RuleApplicability, NotableDateRuleIdentity - the immutable loaded document: a resource of notable-date definitions, each carrying one or more rules (applicability + one strategy + adjustment references + tags).
- CommonNotableDateResources - the resolver delegate over the bundled common catalogues (
global-core,christian-western,global-islamic, …) that authored documents import by name. - INotableDateResourceProvider, MutableNotableDateResourceProvider - supply the current resource for reload; the mutable provider swaps it in via
Reload(...)so aReloadableNotableDateServicepicks up the change. - INotableDateProvider, INotableDateNameLocalizer, NotableDateNameLocalizer - code-first contribution of finished occurrences, and culture-specific display-name localization (applied through NotableDateLocalizationExtensions).
Date-calculation strategies and algorithms - Bodu.Globalization.Calendar.Algorithms
- IDateCalculationStrategy and the strategies a rule maps to: FixedDateStrategy, DayOfWeekInMonthStrategy, RelativeWeekdayInMonthStrategy, WeekdayNearDateStrategy, OffsetFromRuleStrategy, AlgorithmDateStrategy.
- AlgorithmDateStrategy dispatches a named key (
western-easter,orthodox-easter,vernal-equinox,autumnal-equinox,qingming,vesak,asalha-puja,losar,matariki, and the Hindu-festival keys) to the bundled astronomical calculators, falling through to a custom INotableDateAlgorithmRegistry for unknown keys. - INotableDateAlgorithm, INotableDateAlgorithmRegistry, NotableDateAlgorithmRegistry - the custom-algorithm contract (
DateOnly? Calculate(int year)) and its chainable registry. StrategyResolutionContext is the per-resolution context passed to strategies.
Range resolution and observed-date policy - Bodu.Globalization.Calendar.RangeResolution
- ResolutionPolicy - the resource-level policy bundle (
<ResolutionPolicy>in the document) governing duplicates, same-day / span collisions, priority direction, range-inclusion of observed dates, and the working week. - DuplicatePolicy, CollisionPolicy, PriorityDirection, EmissionMode, ObservedDateRangePolicy - the policy vocabulary.
- INotableDateCollisionResolver - a custom resolver consulted when two rules land on the same day under
CollisionPolicy.Custom.
Observance adjustments
- AdjustmentPolicy - a reusable, named shift policy (scope + trigger + action + emission) declared once at the top of a document and referenced by rules via
policyRef. AdjustmentScope constrains where it applies. - AdjustmentTrigger, AdjustmentAction - the trigger / action vocabulary (e.g.
IfWeekend→MoveToNextWorkingDay). - IAdjustmentHandler, IAdjustmentTriggerHandler and their registries (AdjustmentHandlerRegistry, AdjustmentTriggerHandlerRegistry) plus AdjustmentHandlerContext / AdjustmentTriggerContext - the custom-handler model for
AdjustmentAction.Custom/AdjustmentTrigger.Custom.
Working-day arithmetic - Bodu.Extensions
- NotableDateOnlyExtensions (the authoritative
DateOnlysurface), NotableDateTimeExtensions, NotableDateTimeOffsetExtensions -IsWorkingDay,IsNonWorkingDay,IsNotableDate,NextWorkingDay,PreviousWorkingDay,SnapToWorkingDay,AddWorkingDays,WorkingDaysBetween,EnumerateWorkingDays,GetNotableDates, … Each takes anINotableDateService, astring territory, and an optionalBodu.CoreWeekPatternworking week (defaults to Monday-Friday). - NotableDateFiscalExtensions - first / last working day of a fiscal year or quarter for a configurable fiscal-year start month.
Note
The Bodu.Extensions namespace is not auto-imported - add using Bodu.Extensions; to reach the working-day extension methods. By-year resolution (service.Resolve(year, territory)), Localize(...), and WithActualOccurrences() are extension methods in the core Bodu.Globalization.Calendar namespace (NotableDateServiceExtensions, NotableDateLocalizationExtensions, NotableDateSequenceExtensions).
Companion data packs
National public-holiday resources ship in five companion packages, Bodu.Globalization.Calendar.<Region> (all in the Bodu.Globalization.Calendar namespace), so the data can be re-released independently of the runtime:
- Bodu.Globalization.Calendar.Americas -
AR,BR,CA,CL,CO,MX,PE,US(and subdivisions). - Bodu.Globalization.Calendar.Europe - 28 EU/EEA territories including
DE,ES,FR,GB,IE,IT,NL,SE. - Bodu.Globalization.Calendar.AsiaPacific -
AU(with subdivisions),CN,HK,ID,IN,JP,KR,MY,NZ,PH,SG,TH,TW,VN. - Bodu.Globalization.Calendar.MiddleEast -
AE,IL,JO,QA,SA,TR. - Bodu.Globalization.Calendar.Africa -
EG,ET,GH,KE,MA,NG,ZA.
Each pack exposes a static factory - AmericasCalendarData, EuropeCalendarData, AsiaPacificCalendarData, MiddleEastCalendarData, AfricaCalendarData - with SupportedCountries, LoadResource(territory) (returns a NotableDateResource with imports resolved against the bundled common catalogues), and CreateService(territory) (the resource pre-wired into a NotableDateService). See the Calendar data packs guide.
Example
using Bodu.Globalization.Calendar;
using Bodu.Extensions; // working-day extension methods
// 1. Build a service from a companion data pack (loads + validates the resource, resolving imports).
NotableDateService service = AsiaPacificCalendarData.CreateService("AU");
// 2. Resolve every notable date for a year and territory (by-year resolution is an extension method).
foreach (NotableDate d in service.Resolve(2026, "AU-NSW"))
Console.WriteLine($"{d.Date:yyyy-MM-dd} {d.DisplayName} ({d.Category})");
// 3. Resolve a single day or an arbitrary range, optionally filtered.
IReadOnlyList<NotableDate> publicHolidays = service.Resolve(
new DateRange(new DateOnly(2026, 1, 1), new DateOnly(2026, 12, 31)),
"AU-NSW",
NotableDateFilter.ForCategory(NotableDateCategory.PublicHoliday));
// 4. Working-day arithmetic honours the resolved non-working dates (needs `using Bodu.Extensions;`).
DateOnly today = DateOnly.FromDateTime(DateTime.Today);
DateOnly nextOpen = today.NextWorkingDay(service, "AU-NSW");
DateOnly inFive = today.AddWorkingDays(5, service, "AU-NSW");
Notes
- Immutable resource, configured service. Loading produces an immutable, fully validated NotableDateResource; the service is configured through the
init-only collaborators on NotableDateServiceOptions, passed to its constructor. There is no mutable options object - resource-level behaviour (duplicate / collision / observed-date policy, working week) lives in the document's<ResolutionPolicy>. - Nominal vs. observed. A NotableDate tracks both its calculated
ActualDateand its emittedDate, withIsObservedand theAdjustmentPolicyId/AdjustmentReasonpair recording why they differ - so a rule like "if a fixed holiday falls on a weekend, observe it on the next working day" is applied transparently while preserving the original for audit and display. - Thread safety. A
NotableDateServicebuilt from an immutable resource is safe for concurrent reads after construction;ReloadableNotableDateServicereads its provider'sCurrentresource per query and rebuilds atomically when it changes. - Territory containment. Territory is a plain
stringargument ("AU","AU-NSW"). A query for a subdivision includes rules authored for its parent country, so national and regional rules compose naturally. - Target framework.
net8.0. - Extensibility. Implement INotableDateAlgorithm and register it through NotableDateAlgorithmRegistry to back an
<Algorithm key="…">rule; implement INotableDateProvider to contribute finished occurrences from code; layer runtime changes through MutableNotableDateResourceProvider + ReloadableNotableDateService.
Namespaces
Classes
- AdjustmentHandlerContext
Provides an IAdjustmentHandler with the information needed to compute an observed date: the calculated occurrence date, the requesting territory, the firing policy, and access to the surrounding resolution context.
- AdjustmentHandlerRegistry
A mutable registry of custom IAdjustmentHandler implementations keyed by handler key.
- AdjustmentPolicy
Represents a reusable, named adjustment policy that transforms, supplements, or suppresses calculated occurrences.
- AdjustmentScope
Constrains where a reusable adjustment policy may apply: by territory, calendar, category, notable-date concept, or specific rule.
- AdjustmentTriggerContext
Provides an IAdjustmentTriggerHandler with the information needed to decide whether a policy fires: the calculated occurrence date, the requesting territory, the candidate policy, and access to the surrounding resolution context.
- AdjustmentTriggerHandlerRegistry
A mutable registry of custom IAdjustmentTriggerHandler implementations keyed by handler key.
- AfricaCalendarData
Provides access to the embedded Africa notable-date resource pack (South Africa, Nigeria, Kenya, Ghana, Ethiopia, Egypt, and Morocco), built on the notable-date schema.
- AmericasCalendarData
Provides access to the embedded Americas notable-date resource pack (Canada and the United States), built on the notable-date schema.
- AsiaPacificCalendarData
Provides access to the embedded Asia-Pacific notable-date resource pack (Australia, China, Japan, New Zealand), built on the notable-date schema.
- CalculatedEndDateDurationDefinition
Represents an occurrence duration whose end date is calculated by a second date strategy, producing a span that can vary in length from year to year and can cross a civil-year boundary.
- CommonNotableDateResources
Provides the shared, reusable notable-date resources bundled with the library - the civil, Christian, and other thematic catalogues that define common observances (New Year's Day, Easter, Christmas, and the like) once for territory packs to import rather than redefine.
- DistributedNotableDateCacheExtensions
Provides the fluent registration of a distributed (Redis-capable) notable-date cache onto an IServiceCollection.
- EuropeCalendarData
Provides access to the embedded Europe notable-date resource pack (United Kingdom, France, Germany), built on the notable-date schema.
- FixedDurationDefinition
Represents a fixed occurrence duration expressed as a number of calendar days, inclusive of the occurrence start date.
- MiddleEastCalendarData
Provides access to the embedded Middle East notable-date resource pack (the United Arab Emirates, Saudi Arabia, Israel, Turkey, Qatar, and Jordan), built on the notable-date schema.
- MutableNotableDateResourceProvider
An INotableDateResourceProvider whose resource can be replaced at runtime, so a long-lived service observes new data after a reload.
- NotableDate
Represents a single resolved notable-date occurrence, carrying both the emitted date and the source metadata needed for diagnostics and auditability.
- NotableDateBinaryFormatException
The exception thrown when a notable-date binary rule pack cannot be read: the input is not a pack, declares an unsupported format version, is truncated or corrupted, or carries a value outside the sealed format's tables.
- NotableDateBinaryResource
Writes and reads the sealed notable-date binary rule-pack format (
.bcal): a compact, integrity-checked, reflection-free encoding of a validated NotableDateResource.
- NotableDateCacheWarmupExtensions
Provides IServiceCollection registration of the startup notable-date cache warm-up.
- NotableDateCachingExtensions
Provides IServiceCollection extension methods that wrap a registered INotableDateService with a caching decorator.
- NotableDateDefinition
Represents a single notable-date concept: its stable identity, presentation metadata, inherited defaults, and one or more calculation rules.
- NotableDateDurationDefinition
Represents how long a resolved notable-date occurrence lasts: either a fixed number of calendar days or a span whose end date is independently calculated.
- NotableDateFilter
Represents a composable predicate over resolved NotableDate occurrences, used to restrict the results returned by INotableDateService to those matching a category, tag, name, duration, or observed-state criterion. This class cannot be inherited.
- NotableDateLocalizationExtensions
Provides extension methods that apply an INotableDateNameLocalizer to resolved occurrences, replacing each display name with its culture-specific form.
- NotableDateNameLocalizer
A dictionary-backed INotableDateNameLocalizer keyed by notable-date concept identifier and culture, with parent-culture fallback.
- NotableDateResource
Represents a fully loaded and validated notable-date document resource: its resolution policy, reusable adjustment policies, and notable-date concepts.
- NotableDateResourceLoader
Provides the entry points for loading a notable-date document from XML or JSON into a validated NotableDateResource.
- NotableDateRule
Represents one way of calculating a notable-date concept: an applicability scope, exactly one occurrence source (a single-date calculation strategy or a recurrence strategy), an optional duration, and the adjustment policies that transform its occurrences.
- NotableDateSequenceExtensions
Provides extension methods that transform resolved occurrence sequences, such as expanding observed-only results into a timeline that also contains each adjusted occurrence's originally calculated date.
- NotableDateService
Resolves notable-date occurrences from a loaded NotableDateResource for a requested territory and day or date range. This class cannot be inherited.
- NotableDateServiceAsyncExtensions
Provides asynchronous streaming projections over INotableDateService, yielding occurrences incrementally for large multi-year range queries.
- NotableDateServiceCollectionExtensions
Provides IServiceCollection extension methods for registering the Bodu notable-date service.
- NotableDateServiceExtensions
Provides convenience query extension methods over INotableDateService, including a by-year overload that resolves a full civil year.
- NotableDateServiceOptions
Bundles the optional collaborators a NotableDateService consults when a document references custom algorithms, collision resolution, adjustment handlers, trigger handlers, or code-first providers.
- NotableDateValidationDiagnostic
Represents a single diagnostic produced while loading and validating a notable-date document resource.
- NotableDateValidationException
The exception thrown when a notable-date document resource fails to load because one or more error-severity validation diagnostics were produced. This class cannot be inherited.
- ReloadableNotableDateService
An INotableDateService that resolves against the resource currently supplied by an INotableDateResourceProvider, rebuilding its resolution state whenever that resource is reloaded.
- RuleApplicability
Describes where and when a rule applies: its calendar system, optional year bounds, explicit territories, and optional inclusion/exclusion years.
- SqliteNotableDateCacheExtensions
Provides the fluent registration of a SQLite-backed notable-date cache onto an IServiceCollection.
Structs
- DateRange
Represents an inclusive range of calendar days bounded by a start and end date.
- NotableDateRuleIdentity
Represents the full, stable identity of a calculation rule: the owning resource, the notable-date concept, and the rule itself.
- TerritoryCode
Represents a validated territory code composed of an ISO 3166-1 alpha-2 country and an optional subdivision (for example
AUorAU-NSW), with parent/child containment semantics matching the resolution engine.
Interfaces
- IAdjustmentHandler
Computes the observed date of an occurrence for an adjustment policy whose action is Custom, supplementing the engine's built-in actions with caller-supplied logic.
- IAdjustmentHandlerRegistry
Provides lookup of custom IAdjustmentHandler implementations by handler key, binding the Custom action to caller-supplied logic.
- IAdjustmentTriggerHandler
Decides whether an adjustment policy whose trigger is Custom fires for a calculated occurrence, supplementing the engine's built-in triggers with caller-supplied logic.
- IAdjustmentTriggerHandlerRegistry
Provides lookup of custom IAdjustmentTriggerHandler implementations by handler key, binding the Custom trigger to caller-supplied logic.
- INotableDateNameLocalizer
Supplies a culture-specific display name for a resolved notable-date occurrence, supplementing the invariant DisplayName carried by the concept.
- INotableDateProvider
Contributes fully-formed notable-date occurrences to a NotableDateService in code, for sources that cannot be expressed as authored resource rules.
- INotableDateResourceProvider
Supplies the NotableDateResource currently in effect, allowing a long-lived service to observe a resource that is swapped at runtime.
- INotableDateService
Resolves notable-date occurrences - public holidays, observances, and other named dates - for a requested territory and a single day or an inclusive date range.
Enums
- AdjustmentAction
Identifies how an adjustment policy transforms a calculated occurrence once its trigger is active.
- AdjustmentTrigger
Identifies the condition that determines whether an adjustment policy applies to a calculated occurrence.
- CalendarSystem
Identifies the calendar system that a rule's strategy is expressed in.
- CommonNotableDateCatalog
Enumerates the shared, reusable notable-date catalogues bundled with the library, providing a strongly-typed name for each so callers can resolve a catalogue through Resolve(CommonNotableDateCatalog) without spelling its raw resource name.
- DateBoundary
Specifies whether a boundary anchor date is itself part of a calculated occurrence span.
- EndDateSelection
Specifies how a calculated end-date anchor is selected relative to the occurrence's start anchor.
- NotableDateCategory
Provides a coarse-grained classification of a notable date in the notable-date document model.
- NotableDateValidationSeverity
Classifies the severity of a NotableDateValidationDiagnostic produced while loading a notable-date document.
- WeekdayProximity
Identifies the direction and inclusivity used when seeking a weekday relative to an anchor date.