Table of Contents

Bodu.Globalization.Calendar

Bodu.Globalization.Calendar

Bodu.Globalization.Calendar resolves authored calendar rules into concrete notable dates such as public holidays, observances, religious festivals, and regional events. Consumers query dates by year, date, or range and territory, optionally filter by category, tag, or duration, and use the resolved dates for working-day-aware arithmetic. It anchors the Globalization & Calendars topic.

Rules are authored on the notable-date schema as XML or JSON, import from a set of bundled common catalogues, and load eagerly into an immutable, validated resource. More advanced scenarios extend the library with custom algorithms, adjustment handlers, collision resolvers, localizers, and trust-gated plugins.

Calendar package family

The calendar runtime is intentionally small. Region-specific holiday data and dependency-injection registration ship as separate companion packages so they can release on their own cadence without forcing a main-library rebuild.

Bodu.Globalization.Calendar package family - runtime, companions, and data packs

Package Role
Bodu.Globalization.Calendar The runtime - rule engine, resolution pipeline, working-day extensions. Required by every other calendar package.
Bodu.Globalization.Calendar.Builder Fluent, chainable C# API for authoring notable-date documents in code, with XML / JSON serialization and load/save. See the builder guide.
Bodu.Globalization.Calendar.DependencyInjection IServiceCollection extensions for registering INotableDateService over a loaded NotableDateResource. See the DI guide.
Bodu.Globalization.Calendar.Plugins Trust-gated loading of external assemblies that contribute custom date-calculation algorithms. See Building and extending the service.
Bodu.Globalization.Calendar.Americas Curated public-holiday rules for AR, BR, CA, CL, CO, MX, PE, US.
Bodu.Globalization.Calendar.Europe Curated rules for 28 European territories including DE, ES, FR, GB, IT, NL.
Bodu.Globalization.Calendar.AsiaPacific Curated rules for AU (with subdivisions), CN, HK, ID, IN, JP, KR, MY, NZ, PH, SG, TH, TW, VN.
Bodu.Globalization.Calendar.Africa Curated rules for ZA, NG, KE, GH, ET, EG, MA.
Bodu.Globalization.Calendar.MiddleEast Curated rules for AE, SA, IL, TR, QA, JO.

The data packs are independent NuGet packages, so consumers pull in only the regions they need. See the Calendar data packs guide for per-pack install commands, territory coverage, and registration patterns.

Core mental model

NotableDateService resolution pipeline

A single notable date flows through the library in this order:

Notable date flow - from authored document to consumer query

A rule document is authored text on the notable-date schema. NotableDateResourceLoader parses it, resolves its imports against the bundled common catalogues, applies any overrides, validates it, and produces an immutable NotableDateResource. A NotableDateService is built over that resource; for each requested year, date, or range it resolves every applicable rule using the rule's strategy (fixed date, nth weekday, weekday-near-date, offset from another rule, or a named algorithm), runs the referenced adjustment policies against the nominal date, settles same-day collisions, and emits the resolved NotableDate set. Consumers then query that set by territory, category, tag, or date range, or feed it into the working-day extensions.

Key concepts

Concept Plain-language meaning
Document / resource The authored XML/JSON, and the immutable validated value it loads into.
Definition / rule A notable-date concept, and one of its calculation recipes.
Resolution strategy How a rule finds its nominal date - a fixed or positional date, a weekday-in-month rule, a reference to another rule, a working-day rule, or a named algorithm (13 single-date strategies), or one of the four frequency-based recurrence sources. See the strategy reference.
Adjustment policy A reusable post-resolution shift that moves or substitutes the date - e.g. weekend rollover, substitute Monday - referenced by rules.
Territory Geographic scope where the rule applies - AU for Australia, AU-NSW for New South Wales.
Category / tag Classification used for filtering and display. NotableDateCategory is the well-known enum; tags are free-form strings.
Import A pull from a bundled common catalogue, optionally re-scoped via <Use> directives.
Override An ID-targeted add / patch / remove applied at load time; runtime change is a resource reload.
Resolved notable date The concrete NotableDate returned to consumers - observed date, calculated date, name, category, territory, optional multi-day span.

For the full glossary, see Core concepts.

Notable date vs. non-working day

Not every notable date is a non-working day. A rule can describe a public holiday, observance, remembrance day, religious festival, or regional event. Working-day operations use the occurrence's IsNonWorkingDay flag together with the configured working week (a Bodu.Core WeekPattern) to decide whether a date should be skipped.

Territory containment

Territory codes are hierarchical. A query for AU-NSW includes rules authored for AU as well as rules specific to AU-NSW, so national and regional rules compose naturally. The same applies to GB-ENG inheriting GB, US-CA inheriting US, and so on.

Worked example - New Year's Day in the US

A single rule traces the pipeline end-to-end:

  1. global-core defines new-years-day as a <Fixed month="January" day="1" /> rule, PublicHoliday, non-working.
  2. region-us imports it with <Use notableDateRef="new-years-day" territory="US"> and attaches the saturday-to-friday and sunday-to-monday adjustment policies.
  3. The service resolves the fixed date for the requested year - the nominal ActualDate. For 2028 that is 2028-01-01, a Saturday.
  4. The saturday-to-friday policy fires, so the emitted Date is the observed 2027-12-31 (Friday), with IsObserved == true and AdjustmentPolicyId == "saturday-to-friday".
  5. A query for territory US-NY returns the occurrence because US-NY is contained by US.
  6. Working-day arithmetic - someDate.AddWorkingDays(1, service, "US-NY") - skips the observed date because the rule is non-working.

The same flow applies to every other rule - only the strategy in step 3 and the adjustment outcome in step 4 differ.

Common scenarios

Scenario Reach for
Resolve all notable dates in a country for a year service.Resolve(year, territory: "AU")
Resolve notable dates for a specific day or range service.Resolve(new DateOnly(2026, 1, 26), "AU") / service.Resolve(new DateRange(from, to), "AU")
"Is today a public holiday in NSW?" dateOnly.IsNotableDate(service, "AU-NSW", NotableDateFilter.ForCategory(NotableDateCategory.PublicHoliday))
"Add 5 working days to today (skipping weekends and holidays)" dateOnly.AddWorkingDays(5, service, "AU-NSW")
Author rules in XML / JSON and load them NotableDateResourceLoader.Load(xml) / .LoadJson(json)
Author rules fluently in C# (and save to XML / JSON) NotableDateDocumentBuilder - see the builder guide
Compute Easter / Diwali / Vesak for a year author an <Algorithm key="western-easter"> (etc.) rule - see algorithms
Layer ID-targeted edits over imported concepts a document <Overrides> block (<AddRule> / <PatchRule> / <RemoveRule>)
Swap the rule set on a live service MutableNotableDateResourceProvider + ReloadableNotableDateService
Register the service in an IServiceCollection-based host services.AddNotableDateService(resource) from Bodu.Globalization.Calendar.DependencyInjection
Enumerate the territories / calendar systems covered service.GetSupportedTerritories() / service.GetSupportedCalendars()
Load algorithms from external assemblies safely NotableDatePluginLoader + a trust policy
Apply observance adjustments (weekend → next working day) an AdjustmentPolicy referenced by policyRef
Filter resolved notable dates by category, tag, or range NotableDateFilter.ForCategory(...), .WithTag(...), .InDateRange(...) (combine with .And / .Or)

Main types

The same surface, grouped by what role you're playing rather than by namespace.

Types most consumers use

Type Purpose
NotableDateService / INotableDateService Main entry point - resolves and queries notable dates for a date, range, or year.
NotableDate Resolved output - observed date, calculated date, name, category, territory, optional multi-day span.
NotableDateFilter Composable predicate, built via static factory methods (ForCategory, WithTag, WithId, InDateRange, IsNonWorkingDay, …) and combined with And / Or / Not.
TerritoryCode Strongly-typed ISO 3166 country / subdivision code with containment semantics.
NotableDateCategory PublicHoliday / BankHoliday / Observance / Remembrance / Cultural / Religious / Seasonal / Civic / School / Regional / Other / None.
NotableDateOnlyExtensions, NotableDateTimeExtensions Working-day arithmetic over DateOnly / DateTime - IsWorkingDay, NextWorkingDay, AddWorkingDays, WorkingDaysBetween, SnapToWorkingDay, … See Working-day arithmetic.

Types used when authoring and loading rules

Type Purpose
NotableDateResourceLoader Loads XML / JSON (string or Stream) into a validated NotableDateResource.
NotableDateDocumentBuilder Fluent C# authoring of a document - builds, serializes (XML / JSON), and saves; from the Bodu.Globalization.Calendar.Builder companion.
NotableDateResource, NotableDateDefinition, NotableDateRule The immutable loaded document: a resource of definitions, each with one or more rules.
CommonNotableDateResources Resolver over the bundled common catalogues that documents import by name.
ResolutionPolicy Resource-level duplicate / collision / observed-date policy and working week.
AdjustmentPolicy A reusable, named adjustment referenced by rules.

Built-in algorithms

The Bodu.Globalization.Calendar.Algorithms namespace resolves an <Algorithm key="…"> strategy to a bundled calculator. Common keys:

Key Computes
western-easter, orthodox-easter Easter Sunday (Gregorian / Orthodox computus).
vernal-equinox, autumnal-equinox, qingming Sun-longitude based dates.
vesak, asalha-puja, losar Buddhist Vesak / Asalha Puja and Tibetan New Year.
matariki New Zealand Matariki (gazetted table).
diwali, holi, maha-shivaratri, … Hindu lunisolar festivals.

Beneath the <Algorithm key="…"> keys, the same Bodu.Globalization.Calendar.Algorithms namespace holds the date-computation strategy family - one type per resolution-strategy kind. The most common are FixedDateStrategy, DayOfWeekInMonthStrategy, RelativeWeekdayInMonthStrategy, WeekdayNearDateStrategy, OffsetFromRuleStrategy, and AlgorithmDateStrategy (the bridge to a named INotableDateAlgorithm); the full catalogue is 13 IDateCalculationStrategy implementations plus the four IDateRecurrenceStrategy recurrence sources - see the strategy reference. A rule selects its strategy declaratively through the notable-date XML / JSON or the builder; the strategies are resolved by the loader and are not instantiated directly by consumers. See the algorithms guide.

Region-specific holiday rules ship separately in the Bodu.Globalization.Calendar.<Region> data packs (Americas, AsiaPacific, Europe, Africa, MiddleEast) - see Calendar data packs.

Types used when extending the library

Type Purpose
NotableDateServiceOptions Carries the optional service collaborators (Algorithms, CollisionResolver, Handlers, TriggerHandlers, Providers) as init-only slots - passed to the second NotableDateService constructor.
INotableDateAlgorithm, NotableDateAlgorithmRegistry Pluggable algorithm contract and registry, backing <Algorithm key="…"> rules.
INotableDateProvider Code-first contribution of finished occurrences.
MutableNotableDateResourceProvider, ReloadableNotableDateService Runtime resource swap on a live service.
IAdjustmentHandler, IAdjustmentTriggerHandler Custom adjustment action / trigger handlers.
INotableDateCollisionResolver Custom same-day collision behaviour.
INotableDateNameLocalizer Pluggable display-name localization.

Dependency injection

The optional Bodu.Globalization.Calendar.DependencyInjection companion package wires INotableDateService into a Microsoft.Extensions.DependencyInjection container as a singleton. AddNotableDateService has six overloads - a loaded resource or a resource factory, each with or without a NotableDateServiceOptions (or an options factory) carrying the optional collaborators, plus two keyed forms (string serviceKey) for multi-jurisdiction hosts - and AddReloadableNotableDateService has four (resource, resource + options, resource factory + options, and an IOptionsMonitor<TOptions>-driven factory) for the runtime-swap workflow. Resource-level behaviour still lives in the document's <ResolutionPolicy>; the options object only carries collaborators. See the dependency-injection guide.

Advanced extensibility

Plugin loading is intentionally isolated in the separate Bodu.Globalization.Calendar.Plugins package. Use it only when custom notable-date algorithms must be discovered from external assemblies; applications that consume only the built-in algorithms or the curated data packs never need it. Loading is trust-gated and default-deny - an assembly is admitted only when it satisfies an explicit IPluginTrustPolicy (StrongNamePluginTrustPolicy, FileHashPluginTrustPolicy, CompositePluginTrustPolicy, or DelegatingPluginTrustPolicy), and each contributed algorithm must be marked with the NotableDatePluginAttribute. Untrusted or user-writable locations are rejected rather than executed, and a plugin that cannot be admitted or activated surfaces as a clear failure - PluginNotTrustedException, PluginMissingAttributeException, or PluginActivationException - rather than silently resolving. The host, the trust policies, and their failure behaviour are documented in Building and extending the service; a first read of this introduction does not need them.

Where to go next

  • Core concepts - full vocabulary: document vs. resource, rule vs. date, nominal vs. observed, import vs. override, adjustment policy, category vs. tag, working day vs. non-working day.
  • Getting started - install + minimal samples for loading a resource, resolving dates, and working-day arithmetic.
  • Bodu.Globalization.Calendar guides - using NotableDateService, algorithms, rule authoring, working-day arithmetic, territories, data packs.
  • Bodu.Globalization.Calendar API reference - full type-by-type docs.
  • Calendar data packs - region-specific resources (AmericasCalendarData, AsiaPacificCalendarData, EuropeCalendarData, AfricaCalendarData, MiddleEastCalendarData).
  • Globalization & Calendars topic - the runtime together with its companion packages (Builder, DependencyInjection, Plugins) and the regional data packs.