Skip to content

Evaluate date-range overlap and validity

Determine whether DateRange values are individually valid, overlap pairwise or across an ordered sequence, form a contiguous sequence, or contain/are matched by a given date. This operation covers every member of DateRangeExtensions that compares, validates, or filters DateRange values.

Field Value
Operation ID OP-02
Trigger A consumer calls DateRangeExtensions.IsOverlapping, IsValidRange/IsOpenEnded/IsFutureRange/IsPastRange, IsContiguous, IsOverLapping (sequence form), IsInRange, AsOf<T>, ContainsDateRange, or ContainsBeginDate.
Inputs range1, range2 (DateRange), optional inAllowSameDateOverlap (bool, default false); an IEnumerable<DateRange>/IEnumerable<T> where T : DateRange plus a ContiguousOptions/OverlappingOptions/DateTime/DateTime? as appropriate
Outputs A bool result for the validity/overlap/contiguity/membership members; IEnumerable<T> for AsOf<T>.
Primary components DateRangeExtensions.IsOverlapping, .IsValidRange, .IsOpenEnded, .IsFutureRange, .IsPastRange, .IsContiguous, .IsOverLapping, .IsInRange, .AsOf<T>, .ContainsDateRange, .ContainsBeginDate
Entry point src/SAIF.NetCore.Common/SAIF.NetCore.Common/SAIF.NetCore.Common/Extensions/DateRangeExtensions.cs

Steps

Pairwise overlap (IsOverlapping)

  1. Evaluate the leading guard range2 != null, which invokes DateRange's custom != operator with the second operand bound to the null literal; the operator body dereferences that operand's BeginDate unconditionally, so this guard throws NullReferenceException for essentially every call — with a null range2 or a perfectly valid one — before any other step runs. (BR-013)
  2. (Intended algorithm, unreached in ordinary use because of step 1.) Verify both ranges pass IsValidRange (begin <= end). (BR-012)
  3. If either range is invalid: IsOverlapping returns false.
  4. (Intended algorithm, unreached in ordinary use.) Select the comparison mode: Exclusive when inAllowSameDateOverlap is true, otherwise Inclusive.
  5. (Intended algorithm, unreached in ordinary use.) Test whether either range's begin or end date IsBetween the other range's dates, using the selected comparison mode, or whether the two ranges are exactly equal with begin not equal to end.

Single-range validity (IsValidRange/IsOpenEnded/IsFutureRange/IsPastRange)

  1. IsValidRange returns BeginDate <= EndDate directly; this comparison itself cannot throw, but calling IsValidRange on a null DateRange receiver throws NullReferenceException when BeginDate is dereferenced, since the extension method does not null-check range. (BR-012)
  2. IsOpenEnded returns whether BeginDate == DateTime.MinValue || EndDate == DateTime.MaxValue; the same null-receiver caveat applies — a null DateRange throws NullReferenceException on the first property access rather than returning a bool.
  3. IsFutureRange/IsPastRange call IsValidRange first, then compare against DateTime.Now; results depend on the server's current clock and time zone.*

Sequence contiguity (IsContiguous)

  1. Fewer than 2 ranges in the input sequence: return true immediately. (BR-014)
  2. Order the remaining ranges by BeginDate.
  3. Test each consecutive pair against the selected ContiguousOptions policy:
  4. StartMatchesEnd: the current range's begin must equal the previous range's end.
  5. StartIsNextDay (default): the previous range's end plus one day must equal the current range's begin.
  6. AllowGaps: the current range's begin must be at or after the previous range's end.
  7. Return false as soon as any consecutive pair fails the selected policy; otherwise return true after the last pair.

Sequence overlap (IsOverLapping)

  1. Fewer than 2 ranges in the input sequence: return false immediately.
  2. Order the remaining ranges by BeginDate.
  3. On the first iteration, skip the "ignore null date" branch check and fall through directly into the unconditional switch (option) block below — previous is still null at this point.
  4. The switch (option) block dereferences previous.EndDate (DatesMayMatch: range.BeginDate < previous.EndDate; DatesMayNotMatch: range.BeginDate <= previous.EndDate) regardless of loop position, so this dereference of a null previous throws NullReferenceException on the first iteration for any input of 2 or more ranges — the method cannot return normally for its only valid input size.*
  5. (Intended algorithm, unreached because of step 4.) For later iterations, an open-ended previous range followed by a non-MinValue/MaxValue begin date returns true; otherwise the selected OverlappingOptions policy decides whether consecutive ranges overlap.

Range membership and date filtering (IsInRange, AsOf-T, ContainsDateRange, ContainsBeginDate)

  1. IsInRange(DateTime, IncludeComparisons) tests whether date falls within range.BeginDate/EndDate using IComparableExtensions.IsBetween under the selected boundary-inclusion mode (default FirstOnly: begin inclusive, end exclusive). (BR-015)
  2. IsInRange(DateTime?, IncludeComparisons) returns false immediately for a null date, otherwise delegates to step 1.
  3. AsOf<T>(ranges, date, rangeOption) filters ranges with .Where(r => r.IsInRange(date, rangeOption)) and returns every matching item, not just the first. (BR-015)
  4. ContainsDateRange(ranges, range) returns whether any element of ranges has a BeginDate and EndDate both exactly equal to range's, via Any — an exact begin/end membership test, not geometric containment.
  5. ContainsBeginDate(ranges, beginDate) returns whether any element of ranges has a BeginDate exactly equal to beginDate, via Any.

Process flow

The following diagrams visualize each branch family's control flow separately, since they are independent code paths with distinct short-circuits and error conditions.

The first diagram covers IsOverlapping's pairwise-overlap control flow, showing the unconditional guard failure before the intended validity/overlap branches.

flowchart TD
    Start([Call IsOverlapping]) --> NullGuard["Evaluate range2 != null<br>(DateRange operator !=)"]
    NullGuard -->|"Always dereferences<br>the null-literal operand"| Throws([Throws NullReferenceException])
    NullGuard -.->|"Intended, unreached"| ValidCheck{Both ranges<br>IsValidRange?}
    ValidCheck -.->|No| ReturnFalse([Return false])
    ValidCheck -.->|Yes| ModeCheck{inAllowSameDateOverlap?}
    ModeCheck -.->|Yes| Exclusive[Use Exclusive comparison]
    ModeCheck -.->|No| Inclusive[Use Inclusive comparison]
    Exclusive -.-> Test["Test begin/end IsBetween<br>other range"]
    Inclusive -.-> Test
    Test -.-> End([Return bool result])

The second diagram covers the sequence-overlap (IsOverLapping) control flow, showing its own unconditional first-iteration failure, separate from the pairwise IsOverlapping guard above.

flowchart TD
    S_Start([Call IsOverLapping]) --> S_CountCheck{Fewer than<br>2 ranges?}
    S_CountCheck -->|Yes| S_False([Return false])
    S_CountCheck -->|No| S_Order["Order ranges<br>by BeginDate"]
    S_Order --> S_First["First iteration:<br>previous is still null"]
    S_First --> S_Switch["Evaluate switch(option)<br>on previous.EndDate"]
    S_Switch -->|"Always dereferences<br>null previous"| S_Throws([Throws NullReferenceException])
    S_Switch -.->|"Intended, unreached"| S_OpenCheck{Previous open-ended<br>and current not<br>MinValue/MaxValue?}
    S_OpenCheck -.->|Yes| S_True([Return true])
    S_OpenCheck -.->|No| S_PolicyCheck{OverlappingOptions<br>policy satisfied?}
    S_PolicyCheck -.->|Yes| S_True
    S_PolicyCheck -.->|No| S_Continue(["Continue to<br>next range"])

The third diagram covers sequence contiguity (IsContiguous), which short-circuits cleanly and completes end-to-end without the null-dereference defects above.

flowchart TD
    C_Start([Call IsContiguous]) --> C_CountCheck{Fewer than<br>2 ranges?}
    C_CountCheck -->|Yes| C_True([Return true])
    C_CountCheck -->|No| C_Order["Order ranges<br>by BeginDate"]
    C_Order --> C_PairCheck{Each pair satisfies<br>selected policy?}
    C_PairCheck -->|No, any pair| C_False([Return false])
    C_PairCheck -->|Yes, all pairs| C_True

The fourth diagram covers single-range validity (IsValidRange/IsOpenEnded) and range membership/date filtering (IsInRange, AsOf<T>, ContainsDateRange, ContainsBeginDate), all of which complete normally for a non-null receiver/sequence.

flowchart TD
    M_Start(["Call validity or<br>membership member"]) --> M_Which{Which member?}
    M_Which -->|IsValidRange| M_Compare1(["Return BeginDate<br><= EndDate"])
    M_Which -->|IsOpenEnded| M_Compare2(["Return BeginDate==Min<br>or EndDate==Max"])
    M_Which -->|IsInRange| M_Between(["Return date.IsBetween<br>(begin, end, mode)"])
    M_Which -->|AsOf-T| M_Filter(["Return Where filter<br>of matching items"])
    M_Which -->|ContainsDateRange<br>or ContainsBeginDate| M_Any(["Return Any exact-<br>match test"])

Failure and edge-case behavior

  • IsOverlapping throws NullReferenceException from the range2 != null guard for essentially every call, whether range2 is null or a valid, non-null DateRange; the IsValidRange/comparison-mode/IsBetween logic described in the steps above is the intended algorithm but is not reachable in ordinary use.*
  • IsOverLapping (sequence form) throws NullReferenceException for any input of 2 or more ranges, because the switch (option) block dereferences previous.EndDate unconditionally on the first iteration while previous is still null.*
  • IsContiguous returns true (not an exception) for fewer than 2 ranges; this is the one short-circuit case in this operation group that behaves as intended end-to-end.
  • IsValidRange/IsOpenEnded perform direct date comparisons on range's own properties with no null check of their own, so they return a normal bool for any non-null DateRange receiver but throw NullReferenceException if the receiver itself is null.
  • IsFutureRange/IsPastRange depend on DateTime.Now, so results vary with server clock and time zone.*
  • IsInRange(DateTime?, IncludeComparisons) returns false for a null date rather than throwing.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.NetCore.Common/SAIF.NetCore.Common/SAIF.NetCore.Common/Extensions/DateRangeExtensions.cs
  • src/SAIF.NetCore.Common/SAIF.NetCore.Common/SAIF.NetCore.Common/Types/DateRange.cs