Immutable date/time range type with overlap detection, intersection, union, gap finding, and splitting.
dotnet add package Philiprehberger.DateRangeImmutable date/time range type with overlap detection, intersection, union, gap finding, and splitting.
dotnet add package Philiprehberger.DateRange
using Philiprehberger.DateRange;
var meeting = new DateRange(
new DateTimeOffset(2026, 3, 22, 9, 0, 0, TimeSpan.Zero),
new DateTimeOffset(2026, 3, 22, 10, 0, 0, TimeSpan.Zero));
var lunch = new DateRange(
new DateTimeOffset(2026, 3, 22, 11, 30, 0, TimeSpan.Zero),
new DateTimeOffset(2026, 3, 22, 12, 30, 0, TimeSpan.Zero));
Console.WriteLine(meeting.Overlaps(lunch)); // False
Console.WriteLine(meeting.Duration); // 01:00:00
using Philiprehberger.DateRange;
var a = new DateRange(start1, end1);
var b = new DateRange(start2, end2);
if (a.Overlaps(b))
{
var overlap = a.Intersection(b);
Console.WriteLine($"Overlap: {overlap!.Value.Duration}");
}
using Philiprehberger.DateRange;
var ranges = new[] { range1, range2, range3 };
var merged = DateRange.MergeAll(ranges); // Minimal non-overlapping set
using Philiprehberger.DateRange;
var gaps = ranges.FindGaps(); // Returns unoccupied intervals between ranges
using Philiprehberger.DateRange;
var sprint = DateRange.Create(
new DateTimeOffset(2026, 3, 23, 0, 0, 0, TimeSpan.Zero),
new DateTimeOffset(2026, 4, 6, 0, 0, 0, TimeSpan.Zero));
int workdays = sprint.BusinessDays(); // Excludes weekends
var weekdayRanges = sprint.ExcludeWeekends(); // Sub-ranges for weekdays only
using Philiprehberger.DateRange;
var window = DateRange.Create(start, end);
var nextWindow = window.Shift(TimeSpan.FromDays(7)); // Move forward by a week
var blocked = DateRange.Create(blockStart, blockEnd);
var available = window.Subtract(blocked); // 0, 1, or 2 sub-ranges
if (window.IsEmpty)
{
// Sentinel check
}
using Philiprehberger.DateRange;
var range = DateRange.Create(start, end);
bool isQuick = range.IsShorterThan(TimeSpan.FromHours(1));
bool isLong = range.IsLongerThan(TimeSpan.FromDays(7));
bool exactDay = range.DurationEquals(TimeSpan.FromDays(1));
| Method | Description |
|---|---|
DateRange(start, end) | Create a new date range |
DateRange.Create(start, end) | Create a validated date range (throws if start >= end) |
DateRange.MergeAll(ranges) | Merge overlapping ranges into minimal non-overlapping set |
Duration | Get the duration of the range |
Overlaps(DateRange) | Check if two ranges overlap |
Contains(DateTimeOffset) | Check if a point is within the range |
Contains(DateRange) | Check if a range is fully contained |
Intersection(DateRange) | Get the overlapping portion of two ranges |
Union(DateRange) | Merge two overlapping ranges into one |
Gap(DateRange) | Get the gap between two non-overlapping ranges |
IsAdjacent(DateRange) | Check if two ranges are adjacent |
Split(TimeSpan) | Split a range into segments of a given duration |
Shift(TimeSpan) | Translate the range by a positive or negative offset |
Subtract(DateRange) | Return the portions of this range outside the other range |
IsEmpty | True when Start equals End |
DateRange.Empty | A sentinel empty range at DateTimeOffset.MinValue |
BusinessDays() | Count weekdays within the range excluding weekends |
ExcludeWeekends() | Return sub-ranges for weekday-only portions |
IsShorterThan(TimeSpan) | Check if the range duration is less than a time span |
IsLongerThan(TimeSpan) | Check if the range duration is greater than a time span |
DurationEquals(TimeSpan) | Check if the range duration equals a time span |
IEnumerable<DateRange>.Merge() | Merge all overlapping/adjacent ranges |
IEnumerable<DateRange>.FindGaps() | Find all gaps between ranges |
IEnumerable<DateRange>.AnyOverlap() | Check if any ranges overlap |
IEnumerable<DateRange>.TotalDuration() | Get total covered duration accounting for overlaps |
dotnet build src/Philiprehberger.DateRange.csproj --configuration Release
If you find this project useful: