Skip to main content
net10.0

NodaTime support for the CodoMetis.ValueRanges EF Core (Npgsql) plugin: maps LocalDateRange to daterange, LocalDateTimeRange to tsrange, InstantRange to tstzrange and YearMonthRange to a month-aligned daterange (plus their RangeSet<TRange, T> multirange counterparts), bridging through NpgsqlRange<T> via Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime. The full range algebra translates from LINQ to SQL exactly as for the BCL-based types β€” operators, bound accessors, range_merge, range_agg/range_intersect_agg, factories and multirange operations. Enable with one line: options.UseNpgsql(..., npgsql => npgsql.UseValueRangesNodaTime()) β€” this implies both UseNodaTime() and UseValueRanges().

Published
Aug 13, 2026
2mo ago
Total Downloads
1.2K
1,208

Install

dotnet add package CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime --version 5.0.0
ProductVersions
.NET
net10.0

README

CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime

NodaTime support for the CodoMetis.ValueRanges EF Core plugin: maps the range types of CodoMetis.ValueRanges.NodaTime to PostgreSQL range and multirange columns, bridging through NpgsqlRange<T> via Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.

Property type Column type
LocalDateRange daterange
RangeSet<LocalDateRange, LocalDate> datemultirange
LocalDateTimeRange tsrange
RangeSet<LocalDateTimeRange, LocalDateTime> tsmultirange
InstantRange tstzrange
RangeSet<InstantRange, Instant> tstzmultirange
YearMonthRange daterange (month-aligned)
RangeSet<YearMonthRange, YearMonth> datemultirange (month-aligned)

Usage

dotnet add package CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime
options.UseNpgsql(connectionString, npgsql => npgsql.UseValueRangesNodaTime());

UseValueRangesNodaTime() implies both UseNodaTime() (the Npgsql NodaTime plugin, which maps the element types LocalDate, LocalDateTime and Instant) and UseValueRanges() (the base plugin) β€” neither needs to be called separately, and the BCL-based range types keep working in the same model.

The full range algebra translates from LINQ to SQL exactly as documented for the base package β€” operators (@>, &&, <<, -|-, …), bound accessors (lower, upper, lower_inc, upper_inc), Merge (range_merge), the RangeAgg/RangeIntersectAgg aggregates, Intersect/Union/Except, multirange operations including == equality, and the CreateFinite/CreateUnboundedStart/CreateUnboundedEnd factories as guarded range constructor calls:

var day = new LocalDate(2024, 6, 15);

reservations.Where(r => r.Period.Contains(day));            // r."Period" @> DATE '2024-06-15'
reservations.OrderBy(r => r.Period.LowerBound());           // ORDER BY lower(r."Period")
reservations.Where(r => r.Window.Overlaps(other));          // r."Window" && @other
reservations.GroupBy(r => r.CustomerId)
            .Select(g => g.Select(r => r.Period).RangeAgg()); // range_agg(r."Period")

Notes

  • No normalization rules. The base package documents DateTimeKind reinterpretation for tsrange and UTC offset normalization for tstzrange. Neither applies here: LocalDateTime is wall-clock time by construction and Instant is an instant by construction β€” the value written is the value stored.
  • Discrete canonicalization is compensated exactly as for DateRange: LocalDateRange.UpperBound() translates to upper(x) - 1, so server results always equal the in-memory results (verified against live PostgreSQL).
  • YearMonthRange (v5) is stored as a month-aligned daterange: [2025-01, 2025-03] becomes [2025-01-01, 2025-04-01), so no custom database type is needed and every operator works server-side. Bound elements convert through first-of-month dates (Contains(yearMonth) β†’ @> DATE 'yyyy-MM-01'); UpperBound()'s upper(x) - 1 lands on the last day of the end month, which reads back as that month. Reads validate month alignment β€” a partial-month daterange throws instead of silently shifting. The factories cannot be constructed in SQL from column values (months are coarser than the date subtype); constant and parameter ranges work as usual.
  • Precision: NodaTime carries nanoseconds, PostgreSQL stores microseconds β€” sub-microsecond precision is reduced at the database boundary.
  • Instant.MinValue/MaxValue map to PostgreSQL -infinity/infinity by default (Npgsql rule): a finite bound that happens to be infinite, distinct from an unbounded side (upper_inf stays false).
  • External data sources: when the application builds its own NpgsqlDataSource instead of letting EF create one, call UseNodaTime() on that data source builder as well β€” the same requirement Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime documents.
  • Store-name lookups: with the Npgsql NodaTime plugin active, resolving a mapping by store type name alone (e.g. scaffolding-style FindMapping("daterange")) is answered by Npgsql's own plugin (DateInterval). The range types always resolve by CLR type, which is unambiguous.
  • Reverse engineering (dotnet ef dbcontext scaffold) does not produce these types β€” apply them manually after scaffolding, as with the base package.

License

MIT β€” see LICENSE.

Changes from Previous Version

No structural changes between v4.1.0 and v5.0.0.

Adoption Assessment

A verified publisher signal is available. Assess fit from the package's supported frameworks, license and dependency graph rather than popularity alone.

Compatibility metadata lists 1 target framework; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.