Pragmatic.Temporal
Type-safe date/time for .NET 10 with DST-aware arithmetic and explicit timezone handling.
The Problem
Section titled “The Problem”DateTime and DateTimeOffset are ambiguous: DateTime conflates date, time, and zone behind an
easily-ignored Kind; DateTimeOffset keeps an offset but not a zone; calendar arithmetic surprises
(Jan 31 + 1 month); DST transitions cause silent bugs; and DateTime.Now makes tests flaky.
The Solution: Type = Scope
Section titled “The Solution: Type = Scope”The type itself defines the temporal scope, and the compiler enforces it. A LocalDate can’t carry
time; a ZonedDateTime can’t lose its zone.
using Pragmatic.Temporal.Types;
var birthday = new LocalDate(1990, 5, 15); // date onlyvar opening = new LocalTime(9, 0); // time onlyvar appointment = new LocalDateTime(2026, 3, 21, 14, 30); // wall clock, no zonevar flight = ZonedDateTime.FromUtc(DateTimeOffset.UtcNow, "Europe/Rome"); // zone-awareIClock makes “now” injectable and tests deterministic; calendar arithmetic, business days, and DST
transitions are handled correctly.
Packages
Section titled “Packages”| Package | Role |
|---|---|
Pragmatic.Temporal | Core types, calendar arithmetic, business days, cron, IClock |
Pragmatic.Temporal.Json | System.Text.Json converters for all temporal types |
Pragmatic.Temporal.EFCore | EF Core value converters, type mapping, conventions |
Pragmatic.Temporal.AspNetCore | Middleware, timezone detection, model binding |
Pragmatic.Temporal.Testing | TestClock, TestTemporalContext, TestHolidayProvider |
Pragmatic.Temporal.Internationalization | Culture-aware display formatting for LocalDateTime/ZonedDateTime via Pragmatic.Internationalization |
Installation
Section titled “Installation”dotnet add package Pragmatic.TemporalQuick Start
Section titled “Quick Start”using Pragmatic.Temporal.Types;
var checkIn = new LocalDate(2026, 6, 1);var checkOut = checkIn.AddDays(3); // calendar-correctint nights = checkOut.DayNumber - checkIn.DayNumber;
// Inject IClock instead of DateTime.Now → deterministic testspublic class BookingService(IClock clock){ public bool IsPast(LocalDate date) => date < clock.Today;}Full walkthrough: Getting Started.
Status
Section titled “Status”Stable within 1.0.0-alpha — the type set, calendar/business-day arithmetic, DST handling, and the JSON/EF Core/ASP.NET integrations are settled. See the roadmap.
| Concepts | Type = scope, IClock, the mental model, NodaTime comparison |
| Getting Started | First temporal types, arithmetic, injecting IClock |
| Core Types | LocalDate/LocalTime/LocalDateTime/ZonedDateTime/Duration/… — full reference |
| Business Days | Holiday providers, business-day arithmetic, cron |
| DST Handling | Ambiguous/non-existent local times, safe conversions |
| Testing | TestClock and deterministic time |
| JSON Integration | System.Text.Json converters, wire formats, Minimal API setup |
| EF Core Integration | Value converters, column mappings, temporal query extensions |
| ASP.NET Core Integration | Middleware, timezone detection, model binding |
| Common Mistakes | The most frequent date/time pitfalls |
| Troubleshooting | Problem/solution guide |
Requirements
Section titled “Requirements”- .NET 10.0+
License
Section titled “License”Part of the Pragmatic.Design ecosystem — see Licensing. Pragmatic.Temporal is MIT-licensed.