Table of Contents

Bodu.Globalization.Calendar Namespace

Bodu.Globalization.Calendar

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

Companion packages

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 INotableDateService built 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 calculated ActualDate, IsObserved, the rule Identity, DisplayName, TerritoryCode, NotableDateCategory, Priority, DurationDays / EndDate, IsNonWorkingDay, Tags, and the AdjustmentPolicyId / AdjustmentReason audit 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 with And, 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 an ActualAndObserved emission), preserving the standard Resolve ordering.
  • 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-NSW is contained by AU). Implicitly converts to the string territory 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

Date-calculation strategies and algorithms - Bodu.Globalization.Calendar.Algorithms

Range resolution and observed-date policy - Bodu.Globalization.Calendar.RangeResolution

Observance adjustments

Working-day arithmetic - Bodu.Extensions

  • NotableDateOnlyExtensions (the authoritative DateOnly surface), NotableDateTimeExtensions, NotableDateTimeOffsetExtensions - IsWorkingDay, IsNonWorkingDay, IsNotableDate, NextWorkingDay, PreviousWorkingDay, SnapToWorkingDay, AddWorkingDays, WorkingDaysBetween, EnumerateWorkingDays, GetNotableDates, … Each takes an INotableDateService, a string territory, and an optional Bodu.Core WeekPattern working 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 ActualDate and its emitted Date, with IsObserved and the AdjustmentPolicyId / AdjustmentReason pair 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 NotableDateService built from an immutable resource is safe for concurrent reads after construction; ReloadableNotableDateService reads its provider's Current resource per query and rebuilds atomically when it changes.
  • Territory containment. Territory is a plain string argument ("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

Bodu.Globalization.Calendar.Algorithms
Bodu.Globalization.Calendar.Builder
Bodu.Globalization.Calendar.Caching
Bodu.Globalization.Calendar.Plugins
Bodu.Globalization.Calendar.RangeResolution
Bodu.Globalization.Calendar.Tool

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 AU or AU-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.