aoakeson
aoakeson

Reputation: 602

Continuous Overlapping of Multiple Date Ranges C#

I am working on an algorithm to calculate a continuous overlap of multiple date ranges. It also needs to have a set number of times that it overlaps. For the example image below, I need 3 dates to overlap continuously. The valid overlapping dates would be Aug 20 - Aug 23, as Aug 24 only has 2 overlapping.

I have attempted many approaches including looping through all dates and comparing each one indivually with the next. That code looks like this.

Here is a .net fiddle for better visualization: https://dotnetfiddle.net/x3LfHR#.

    private bool Compare(CompareDate a, CompareDate b)
    {
        DateTime? tStartA = a.ActiveDate;
        DateTime? tEndA = a.ExpireDate;
        DateTime? tStartB = b.ActiveDate;
        DateTime? tEndB= b.ExpireDate;

        bool overlap = (tStartA <= tEndB || tEndB == null) && (tStartB <= tEndA || tEndA == null);

        DateTime? overlapStart = null;
        DateTime? overlapEnd = null;

        if (overlap)
        {
            //Find maximum start date. 
            overlapStart = (tStartA >= tStartB) ? tStartA : tStartB;
            //Find Min End date between the two
            overlapEnd = (tEndA <= tEndB) ? tEndA : tEndB;

            if (overlapStart > this.overlapStart || this.overlapStart == null)
            {
                this.overlapStart = overlapStart;
            }
            if (overlapEnd < this.overlapEnd || this.overlapEnd == null)
            {
                this.overlapEnd = overlapEnd;
            }

However, this approach makes it tricky to figure out continuous overlap dates. I have attempted to use the .Net Time period library at https://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET, but its not relevant in my case. Any help is appreciated.

enter image description here

Upvotes: 1

Views: 1396

Answers (2)

code4life
code4life

Reputation: 15794

OK - LINQ to the rescue!

Note: In order to make the comparison work, you must remove the time component and strictly use the only the date (e.g. DateTime.Date). Based on your requirements, that's exactly how you need to do it, so it shouldn't be a problem.

public List<DateTime> CompareDates(List<DateTime[]> compareRanges, int overlapLevel = 1)
{
    var grouped = compareRanges.SelectMany(r => r).GroupBy(d => d);
    var thresholdMatch = grouped.Where(g => g.Count() >= overlapLevel)
        .Select(g => g.Key)
        .OrderBy(d => d)
        .ToList();

    return thresholdMatch;
}

You can test the logic in a sample console app, using the skeleton code below as an example:

static void Main()
{
    var setA = new[]
    {
        new DateTime(2017, 8, 20),
        new DateTime(2017, 8, 21),
        new DateTime(2017, 8, 22),      
        new DateTime(2017, 8, 23),      
        new DateTime(2017, 8, 24),
    };

    var setB = new[]
    {
        new DateTime(2017, 8, 20),
        new DateTime(2017, 8, 21),
        new DateTime(2017, 8, 22),
    };

    var setC = new[]
    {
        new DateTime(2017, 8, 22),
        new DateTime(2017, 8, 23),
        new DateTime(2017, 8, 24),
        new DateTime(2017, 8, 25),
        new DateTime(2017, 8, 26),
    };

    var setD = new[]
    {
        new DateTime(2017, 8, 20),
        new DateTime(2017, 8, 21),
        new DateTime(2017, 8, 22),
        new DateTime(2017, 8, 23),
    };

    var compareList = new List<DateTime[]>
    {
        setA, setB, setC, setD
    };

    // setting the threshold to 2 will cause 8/24 to be added to the result...
    // setting this to 1 (default) will return all intersections
    // for now, set it to 3, per the question!
    var result = CompareDates(compareList, 3); 
    foreach (var intersectDate in result)
    {
        Console.WriteLine(intersectDate);
    }
}

Hope this helps, I certainly had fun with it!

P.S. I forked your fiddle: https://dotnetfiddle.net/GUzhjh. This contains the modified version of your original program, so you should be able to play around with it a bit.

Upvotes: 1

Pieter Geerkens
Pieter Geerkens

Reputation: 11883

Here is a start at an algorithm:

  • Sort all start and end datetimes, assigning a +1 to every start and -1 to every end.
  • proceed from interval-start to interval-end, aggregating the period assignments above looking for a +3 value. Note this datetime.
  • proceed forward until your aggregate value drops below +3.
  • Voila! (I think.) Remember this one and continue processing.
  • When a second occurs, then save the longest and discard the other.
  • Continue until end-interval found; and report result.

Upvotes: 0

Related Questions