CodoMetis.ValueRanges 6.3.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package CodoMetis.ValueRanges --version 6.3.0
                    
NuGet\Install-Package CodoMetis.ValueRanges -Version 6.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CodoMetis.ValueRanges" Version="6.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CodoMetis.ValueRanges" Version="6.3.0" />
                    
Directory.Packages.props
<PackageReference Include="CodoMetis.ValueRanges" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CodoMetis.ValueRanges --version 6.3.0
                    
#r "nuget: CodoMetis.ValueRanges, 6.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CodoMetis.ValueRanges@6.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CodoMetis.ValueRanges&version=6.3.0
                    
Install as a Cake Addin
#tool nuget:?package=CodoMetis.ValueRanges&version=6.3.0
                    
Install as a Cake Tool

CodoMetis.ValueRanges

In-memory range and set types for .NET 10 — the complete PostgreSQL interval and membership algebra with no database dependency.

Two type families, both immutable and canonical at construction:

  • Range types — the six value domains PostgreSQL ships as built-in range types, plus TimeRange, each modelled as a discriminated union of five sealed variants, with RangeSet<TRange, T> as their always-normalized multirange counterpart.
  • Value sets (v6) — canonical sets of scalar values whose PostgreSQL storage shape is a native array.

Everything executes in process. The companion package CodoMetis.ValueRanges.EFCore.PostgreSQL maps both families to PostgreSQL columns and translates the algebra to SQL, so the same code works in memory and as a query.

dotnet add package CodoMetis.ValueRanges

Requires .NET 10 or later.

Range types

.NET type PostgreSQL equivalent Element type Discrete
Int32Range int4range int
Int64Range int8range long
DecimalRange numrange decimal
DateRange daterange DateOnly
DateTimeRange tsrange DateTime
DateTimeOffsetRange tstzrange DateTimeOffset
TimeRange timerange (custom) TimeOnly

NodaTime equivalents — LocalDateRange, LocalDateTimeRange, InstantRange and the month-granularity YearMonthRange — live in CodoMetis.ValueRanges.NodaTime.

Unboundedness is a shape, not a bound value

Each range is a discriminated union of Finite, UnboundedStart, UnboundedEnd, EmptyRange and Infinity. The shape lives in the static type: UnboundedEnd has no End property to put a sentinel in, Finite has no flag to disown its End, and neither carries a nullable bound. Invalid states are unrepresentable, and pattern matching is exhaustive with compiler-enforced coverage.

DateTimeRange.CreateUnboundedEnd(start)               // genuinely open-ended
DateTimeRange.CreateFinite(start, DateTime.MaxValue)  // ends at a specific instant

The two are not interchangeable and the compiler will not let them be confused — a distinction a bounds-plus-flags representation has to reconcile at runtime, and one that still matters at the database boundary, where DateTime.MaxValue maps to PostgreSQL infinity.

Operations

Containment, overlap and adjacency (Contains, Overlaps, IsContainedBy, IsAdjacentTo), directional comparisons (IsStrictlyLeftOf/RightOf, DoesNotExtendLeftOf/RightOf), set operations (Intersect, Union, Except, Complement, Merge), bound accessors (LowerBound/UpperBound/LowerBoundInclusive/UpperBoundInclusive, matching PostgreSQL's NULL semantics) and the RangeAgg/RangeIntersectAgg aggregates — all matching PostgreSQL's results exactly, including its canonicalization of discrete ranges.

Why these element types

Interval algebra needs a total order the type's own comparisons agree with and, for adjacency, a defined step. double and float have neither and fail quietly: double.CompareTo reports NaN as less than every value and equal to itself, while the IEEE operators disagree, so a library generic over IComparable<T> accepts double and answers containment against a NaN bound with a straight face. Restricting the element types to a vetted set is what makes the algebra sound.

Value sets (v6)

A value set is an immutable, canonical set of scalar values — deduplicated, sorted, never containing null, with structural equality. It relates to a PostgreSQL array column exactly as RangeSet<DateRange, DateOnly> relates to datemultirange: the CLR type models the domain concept, the column is its storage encoding.

.NET type Element type PostgreSQL column Wrapper arity
StringSet string text[] StringSet<TElement>
GuidSet Guid uuid[] GuidSet<TElement>
Int16Set short smallint[]
Int32Set int integer[] Int32Set<TElement>
Int64Set long bigint[] Int64Set<TElement>
DecimalSet decimal numeric[]
DateSet DateOnly date[]
TimeSet TimeOnly time[]
DateTimeSet DateTime timestamp[]
DateTimeOffsetSet DateTimeOffset timestamptz[]
var tags = StringSet.From("beta", "alpha", "beta");   // {alpha,beta} — deduplicated, sorted
StringSet more = ["gamma", "alpha"];                  // collection expressions work

tags.Contains("alpha");      // true
tags.Overlaps(more);         // true — shares "alpha"
tags.IsProperSubsetOf(more); // false — proper containment excludes equality
tags.Union(more);            // {alpha,beta,gamma}
tags.Count;                  // 2

The wrapper arities take generator-produced domain values — Vogen, Metalama, StronglyTypedId or hand-written — and are constrained only on BCL interfaces, so your domain types never reference this package.

Canonical form is the contract

Every construction path deduplicates and sorts: From, parsing, JSON, and materialization from the database. That is load-bearing twice — the EF ValueComparer collapses to a cheap equality with no false diffs, and SQL = on the stored array coincides with set equality.

String-backed sets sort ordinal, never culture-sensitive: canonical form is a cross-writer storage contract, not a display order, and a culture sort would make two machines disagree about the same set. Everything else sorts by the element's own comparison.

Intersect, Except and Add are client-side only — PostgreSQL's array type has no intersection, difference or sorted insert.

Literals, parsing and JSON

Every type implements IParsable<T> and IFormattable using PostgreSQL literal syntax, and ships System.Text.Json converters for direct use in ASP.NET Core APIs:

Int32Range.Parse("[1,10)", null).ToString();   // [1,10)
StringSet.Parse("{alpha,beta}", null);         // {alpha,beta}

options.AddRangeConverters();                  // registers both families

Documentation

The full README covers every operation with examples, the multirange operator matrix, the parsing and JSON rules, and the EF Core mapping. See also the changelog.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on CodoMetis.ValueRanges:

Package Downloads
CodoMetis.ValueRanges.EFCore.PostgreSQL

Entity Framework Core (Npgsql) plugin for CodoMetis.ValueRanges: maps the range types to the PostgreSQL range columns (int4range, int8range, numrange, daterange, tsrange, tstzrange, and the custom timerange) and RangeSet<TRange, T> to the corresponding multirange columns — no manual value converters required. Translates the full range algebra from LINQ to SQL: Contains, Overlaps, IsContainedBy, IsStrictlyLeftOf/RightOf, DoesNotExtendLeftOf/RightOf, IsAdjacentTo (@>, <@, &&, <<, >>, &<, &>, -|-) on ranges and multiranges, bound accessors (lower, upper, lower_inc, upper_inc), Merge (range_merge), the RangeAgg/RangeIntersectAgg aggregates (range_agg, range_intersect_agg), Intersect (*), Union and Except (multirange + and -), RangeSet operations and operators including == equality, and the CreateFinite/CreateUnboundedStart/CreateUnboundedEnd factories as range constructor calls. v6 also maps the value set types to native PostgreSQL arrays (StringSet to text[], GuidSet to uuid[], …) by convention — wrapper instantiations like StringSet<YourKey> recognized automatically, nothing to configure — and translates the set algebra to the array operators: Contains as GIN-servable containment (@> ARRAY[value]), Overlaps (&&), IsSubsetOf (<@), IsSupersetOf (@>), Union (array_cat), Count/IsEmpty (cardinality). Enable with one line: options.UseNpgsql(..., npgsql => npgsql.UseValueRanges()).

CodoMetis.ValueRanges.NodaTime

NodaTime range types for CodoMetis.ValueRanges: LocalDateRange (daterange), LocalDateTimeRange (tsrange), InstantRange (tstzrange) and YearMonthRange — a month-granularity range for billing and reporting periods, stored as a month-aligned daterange by the EF Core companion. Each range type is a discriminated union of five sealed variants (Finite, UnboundedStart, UnboundedEnd, EmptyRange, Infinity) with the full interval algebra of the core package: Contains, Overlaps, IsAdjacentTo, Intersect, Union, Except, Merge, bound accessors, RangeAgg/RangeIntersectAgg aggregates, RangeSet<TRange, T> multiranges, PostgreSQL literal parsing/formatting and System.Text.Json support. Includes conversions to and from NodaTime's own Interval and DateInterval. LocalDateTime is wall-clock time by construction and Instant is an instant by construction, so the DateTimeKind and offset-normalization caveats of the BCL-based types do not arise. v6 adds the NodaTime value sets — LocalDateSet (date[]), LocalDateTimeSet (timestamp[]), InstantSet (timestamptz[]), LocalTimeSet (time[]) and the month-granularity YearMonthSet (month-aligned date[]): immutable, canonical (deduplicated, sorted, null-free) sets with the full membership algebra, ISO-calendar normalization at construction, PostgreSQL array literals and JSON support. v6.4 adds a validated-wrapper arity for each of them (LocalDateSet<T>, LocalDateTimeSet<T>, InstantSet<T>, LocalTimeSet<T>, YearMonthSet<T>), constrained only on BCL interfaces, so generator-produced domain values never reference this package.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
8.0.0 173 8/17/2026
7.0.0 168 8/17/2026
6.3.0 132 8/16/2026
6.2.1 150 8/16/2026
6.2.0 215 8/15/2026
6.1.0 123 8/14/2026
6.0.0 122 8/14/2026
5.0.0 118 8/13/2026
4.1.0 119 8/12/2026
4.0.0 104 8/10/2026
3.1.0 156 6/17/2026
3.0.0 134 6/11/2026
2.0.0 127 6/11/2026
1.0.0 124 6/10/2026