Runnable samples
The repository ships runnable, self-contained sample projects for the calendar packages under
samples/Globalization.Calendar/.
Every sample is offline and deterministic by construction - the rule data ships as embedded
XML resources in the engine and data-pack assemblies, so there is nothing to configure and no
network to reach. The samples are members of bodu.slnx and are built and executed by CI, so
the code they show cannot drift from the current API. Each sample's README documents every
scenario individually: its intent, what the code does, the output to expect, and the APIs
demonstrated.
Run any sample from the repository root:
dotnet run --project samples/Globalization.Calendar/<SampleName>
The samples
Bodu.Globalization.Calendar.Samples.NotableDatesBasics
The front door: AsiaPacificCalendarData factories over the
embedded packs, year/day/range queries through
INotableDateService, ISO 3166-2 subdivision shadowing (AU vs
AU-VIC/AU-NSW Labour Day, pinned to published dates), composable
NotableDateFilter predicates, observed-date substitution
with full lineage (IsObserved / ActualDate / AdjustmentReason - the AU 2021 Christmas
double-substitution), and expanding an observed-only result into the full actual + observed
timeline with WithActualOccurrences() on
NotableDateSequenceExtensions. Packages:
Bodu.Globalization.Calendar, Bodu.Globalization.Calendar.AsiaPacific.
Bodu.Globalization.Calendar.Samples.WorkingDays
The Bodu.Extensions business-day surface over a service: predicates, T+2 settlement with
AddWorkingDays, next/snap navigation, WorkingDaysBetween counting and lazy enumeration,
fiscal-period boundaries, and WeekPattern overrides for non-Mon-Fri working weeks. Packages:
Bodu.Globalization.Calendar, Bodu.Globalization.Calendar.AsiaPacific.
Bodu.Globalization.Calendar.Samples.CustomCalendar
The "bring your own data" story:
NotableDateDocumentBuilder fluent authoring - fixed
and floating rules, a calculated-duration year-end shutdown (UntilDate), and
frequency-based recurrences (daily-interval, weekly, monthly day, and monthly ordinal-weekday
schedules) - declarative weekend-roll adjustment policies, importing the shared catalogues through
CommonNotableDateResources (including the offset-anchor
dependency the validator enforces), and the XML and JSON save/load round trips that make the
document a distributable artifact. Its FrequencyBasedSchedules and AuthoringCompanyHolidays scenarios are
the runnable companions to the Notable-date rule strategies
guide. Packages: Bodu.Globalization.Calendar, Bodu.Globalization.Calendar.Builder.
Bodu.Globalization.Calendar.Samples.ServiceHosting
Container wiring: AddNotableDateService singleton registration, and
AddReloadableNotableDateService with a mid-run
MutableNotableDateResourceProvider Reload swap - held
service references immediately serve the new data. Packages: Bodu.Globalization.Calendar,
Bodu.Globalization.Calendar.DependencyInjection, Bodu.Globalization.Calendar.AsiaPacific.
Bodu.Globalization.Calendar.Samples.CustomAlgorithm (+ .Test)
Extending the date-calculation vocabulary: a consumer-written
INotableDateAlgorithm registered on a
NotableDateAlgorithmRegistry, referenced
declaratively from rules (Algorithm("key")), validated by the loader and dispatched by the
service - plus a five-line lambda adapter for one-off calculations. Packages:
Bodu.Globalization.Calendar, Bodu.Globalization.Calendar.Builder.
Bodu.Globalization.Calendar.Samples.Caching
The caching layer: the read-through
CachingNotableDateService decorator over the
in-memory and JSON / TOML file backends (whole-territory, civil-year cache entries - warm queries,
sub-range clipping, and filtered overloads all served without re-resolving; a file-backed cache
lets a fresh service instance start warm), explicit Warm pre-resolution of a serving window,
and AddCachedNotableDateService decorating an already-registered INotableDateService. The
durable backends now run rather than being described: a SQLite-backed cache is read back by a
second service after the first is disposed, and two services over one IDistributedCache
stand in for two processes sharing a store - both proved by the engine call count not moving.
The hosted warm-up remains a fenced comment block. A
CountingNotableDateService wrapper counts the resolutions that reach the real engine, so every
cache hit is proved by call count rather than by timing. Packages: Bodu.Globalization.Calendar,
Bodu.Globalization.Calendar.Caching, Bodu.Globalization.Calendar.DependencyInjection,
Bodu.Globalization.Calendar.AsiaPacific.
Bodu.Globalization.Calendar.Samples.RegionalData
The five regional data packs - Bodu.Globalization.Calendar.Americas, .AsiaPacific, .Europe,
.MiddleEast, and .Africa. Lists what each pack covers (63 territories between them) and
resolves a year from each, then shows why a notable-date lookup is territory-scoped: 1 January is
a public holiday in all five sample territories while 25 December is not one in AE at all. A
closing pass prints AU's 2027 weekend substitutions with the ActualDate each shifted from, so
the adjustment is auditable rather than implied. Every pack is an embedded resource, so nothing
here reads a file or makes a request. Packages: the five Bodu.Globalization.Calendar.<Region>
data packs.
Bodu.Globalization.Calendar.Samples.Plugins (+ .Plugin.Contoso)
Trust-gated plugin loading with Bodu.Globalization.Calendar.Plugins. A date-calculation algorithm
is loaded out of a separate assembly at runtime and registered into a
NotableDateAlgorithmRegistry, after which a rule
document referencing it by key (contoso.founding-day) resolves alongside ordinary fixed rules -
neither side needing a type from the other. The second scenario is the reason the package exists:
loading a plugin executes arbitrary code in-process, so the loader takes no policy-free overload.
It pins the assembly with a SHA-256 allow-list, shows the same load refused under a wrong hash with
PluginNotTrustedException raised before any plugin code runs, then a DelegatingPluginTrustPolicy
for a host's own rule and a CompositePluginTrustPolicy that can only narrow trust.
The plugin is its own project, …Samples.Plugin.Contoso, referenced with
ReferenceOutputAssembly="false" so its types are genuinely unavailable to the host at compile
time - the host reaches it only through the loader. That project is a class library with no entry
point and is not run by the samples sweep. Packages: Bodu.Globalization.Calendar.Plugins.
Bodu.Globalization.Calendar.Samples.RulePackToolchain
The rule-pack toolchain - Bodu.Globalization.Calendar.Build and the bodu-calendar tool from
Bodu.Globalization.Calendar.Tool. Unlike every other sample the interesting part happens at
build time: the csproj declares rules/company-holidays.xml as a NotableDatePack item, the
CompileNotableDatePack task lints and compiles it with the tool, and the sealed .bcal pack is
copied beside the application. The program is the consumer side, loading that pack with
LoadBinary and comparing it against
parsing the same XML at runtime - same three holidays, 255 bytes against 1,946, and no schema
validation on the deployment path. A NuGet consumer writes only the item group; the sample's
explicit .targets import and task/tool path overrides are repository plumbing, needed because
here the task and tool are projects rather than a restored package. Packages:
Bodu.Globalization.Calendar.Build, Bodu.Globalization.Calendar.Tool.
Bodu.Globalization.Calendar.Samples.ValidationLint
Collect-mode linting: NotableDateDocumentBuilder.Validate() / TryBuild(...) on clean,
semantically invalid, and structurally incomplete builders, and
NotableDateResourceLoader.TryLoad over arbitrary rule-pack text - malformed XML surfacing as
BODU-CAL-SYNTAX, semantic problems as their stable codes, and a valid pack loading - every
problem reported as a NotableDateValidationDiagnostic with a
stable BODU-CAL-* code instead of an exception, the shape build tasks and editor integrations
want. The code catalogue lives in the
validation diagnostics guide. Packages:
Bodu.Globalization.Calendar, Bodu.Globalization.Calendar.Builder.
Plugins, by choice
No sample ships a plugin assembly - a single-project plugin demo would have to load itself under
the dev-only AllowAllPluginTrustPolicy, which is exactly what production hosts must not do.
Instead, the CustomAlgorithm sample carries a fenced comment block showing the real switch: add
Bodu.Globalization.Calendar.Plugins, mark the plugin assembly with
[assembly: NotableDatePlugin(...)], and load it through
NotableDatePluginLoader under a pinned
StrongNamePluginTrustPolicy, registering the discovered algorithms into the same registry the
sample already uses.
Testing your own data pack
Bodu.Globalization.Calendar.Samples.CustomAlgorithm.Test shows the in-repo pattern for
validating a consumer-built calendar: shape it as a data-pack factory (SupportedCountries /
LoadResource / CreateService - see the sample's ContosoCalendarData), derive
CalendarDataTestsBase from the Bodu.Globalization.Calendar.Data.Test.Common project - the
same base every regional <Region>CalendarData test derives - and add known-answer [DataRow]
rows pinning deterministic dates (exact assertions for Gregorian/weekend-roll rules; the base's
AssertWithinDays for lunar/astronomical tolerance). The base contributes the
load-and-resolve smoke contract; the rows pin your rules to published expectations.
The test-support project is repository-internal today (it is not packaged); if you consume the
packages from NuGet, copy the small base class into your test project - it depends only on the
engine and MSTest.TestFramework.