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;
}
}
}