How check intersection of DateTime periods [duplicate]

I have four DateTime objects. A1, A2 and B1, B2.

I need to know that the period A1-A2 doesn't intersect with period B1-B2. But I don`t want to write dirty code, like many if blocks.

if (A1 < B1 && A2 > B1)
{
    return false;
}

.... etc.

EDITED

I tried to use this one: Comparing ranges

DateTime A1 = DateTime.MinValue.AddMinutes(61);
DateTime A2 = DateTime.MinValue.AddHours(1.2);
DateTime B1 = DateTime.MinValue.AddMinutes(5);
DateTime B2 = DateTime.MinValue.AddHours(1);

Console.WriteLine(Range.Overlap(
    new Range<DateTime>(A1, A2),
    new Range<DateTime>(B1, B2)
));

It returned true but I expected false. Because this code always returns true

 if (left.Start.CompareTo(left.Start) == 0)
 {
     return true;
 }

Solution 1:

If in your program the ranges A1-A2 and B1-B2 are "proper" in the sense that it is known that A1<=A2 and B1<=B2

then your non-intersection test is simply

if(A1>B2 || B1>A2)

Note I have glossed over whether this is > or >=. The proper choice of operator depends on how you have defined your ranges to include or exclude their endpoints; i.e. whether they represent closed, open, or half-open intervals.

Solution 2:

I dont believe there is going to be any manner of 'easy' code to write; you have to account for 4 distinct use cases. If you need to do this kind of check a lot, I'd write an extension method. Otherwise, you just need to check these conditions:

 |--- Date 1 ---|
      | --- Date 2 --- |


      | --- Date 1 --- |
 | --- Date 2 ---- |


 | -------- Date 1 -------- |
      | --- Date 2 --- |

      | --- Date 1 --- |
 | -------- Date 2 -------- |

EDIT: To provide actual code:

public class DateTimeRange
{
     public DateTime Start { get; set; }
     public DateTime End { get; set; }

     public bool Intersects(DateTimeRange test)
     {
         if(this.Start > this.End || test.Start > test.End)
            throw new InvalidDateRangeException();

         if(this.Start == this.End || test.Start == test.End)
              return false; // No actual date range

         if(this.Start == test.Start || this.End == test.End)
              return true; // If any set is the same time, then by default there must be some overlap. 

         if(this.Start < test.Start)
         {
              if(this.End > test.Start && this.End < test.End)
                  return true; // Condition 1

              if(this.End > test.End)
                  return true; // Condition 3
         }
         else
         {
              if(test.End > this.Start && test.End < this.End)
                  return true; // Condition 2

              if(test.End > this.End)
                  return true; // Condition 4
         }

         return false;
    }
}

That should cover the use cases.

Solution 3:

Time Period Library for .NET looks interesting.

Methods like IsSamePeriod, HasInside, OverlapsWith, or IntersectsWith are available for convenience to query for special, often used variants of such period relations.

Solution 4:

My approach is to create a class called Period which contains Start and End properties (DateTime). This class can have methods or extension methods to calculate things like intersections. Let's say you have a method like this in your Period class:

public bool IntersectsWith(Period otherPeriod)
{
    return !(this.Start > otherPeriod.End || this.End < otherPeriod.Start);
}

Then you can write code like this:

if (!periodA.IntersectsWith(periodB))
{
    return false;
}

Solution 5:

The code which you tried had bug, I have fixed it:

Try this:

class Range<T> where T : IComparable<T>
{
    public T Start { get; private set;}
    public T End { get; private set;}

    public Range(T start, T end)
    {
        //Always ensure that Start < End
        if(start.CompareTo(end) >= 0)
        {
            var temp = end;
            end = start;
            start = temp;
        }

        Start = start;
        End = end;
    }
}

static class Range
{
    //Based on Eric's idea of doing negative check to figure out
    //how many ways there are for ranges to NOT overlap.
    public static bool EricOverlap<T>(Range<T> left, Range<T> right)
        where T : IComparable<T>
    {
        if (right.Start.CompareTo(left.End) > 0)
            return false;

        if (left.Start.CompareTo(right.End) > 0)
            return false;

        return true;
    }
    public static bool Overlap<T>(Range<T> left, Range<T> right)
        where T : IComparable<T>
    {
        if (left.Start.CompareTo(right.Start) == 0)
        {
            return true;
        }

        else if (left.Start.CompareTo(right.Start) > 0)
        {
            return left.Start.CompareTo(right.End) <= 0;
        }
        else
        {
            return right.Start.CompareTo(left.End) <= 0;
        }
    }
}