0

I am kind off newbie in C# and working on some sort of logic where I have a range of date (suppose for a month, i.e. 2018-06-01 to 2018-06-30).

And I have a list of minor date ranges:

STARTDATE  ENDDATE
2018-06-04 2018-06-06
2018-06-11 2018-06-14
2018-06-17 2018-06-20

Lets assume above dates are needed to be blocked from the original one month range, 2018-06-01 to 2018-06-30. So as an output I need some logic to return the available ranges of dates that aren't blocked!

So the output needs to be like:

STARTDATE  ENDDATE
2018-06-01 2018-06-03
2018-06-07 2018-06-10
2018-06-15 2018-06-16
2018-06-18 2018-06-19
2018-06-21 2018-06-30

So I need some C# experts guidance for how should I implement this. I am not asking for code but maybe if someone have written a similar logic then please share the idea or provide any reference to the same.

My main concern is should I use list, dictionary, data table or something else?

PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
Zhrez Pain
  • 327
  • 1
  • 2
  • 10
  • Possible duplicate of [How to get gap in date ranges from a period of time](https://stackoverflow.com/questions/27852726/how-to-get-gap-in-date-ranges-from-a-period-of-time) – Peter B Aug 22 '18 at 15:44
  • I would probably use a [Tuple](https://learn.microsoft.com/en-us/dotnet/csharp/tuples) to do what you're trying to do. The logic you seem to be looking for would be to simply start with the first date in said month, and loop through the list "minor date ranges" and check the start date. `if` the first of the month isn't equal to the first element in the "minor date ranges", you have your first element for `STARTDATE` and you can keep looping through the days to find the `ENDDATE` of the first gap. Rinse and repeat until the end of the month. – Chris Aug 22 '18 at 15:47
  • I think of the ranges in the result in your example is incorrect: `2018-06-18 2018-06-19` – PiotrWolkowski Aug 22 '18 at 17:30

3 Answers3

0

You can use Linq, except FilteredList = BiggerList.Except(SmallerList).ToList()

PiJei
  • 584
  • 4
  • 19
  • Not sure, ranges are not single tuple or cell. STARTDATE and ENDDATE are different columns or fields, you can say. – Zhrez Pain Aug 22 '18 at 15:44
0

I would go with a list for excluded dates, a list for allowed dates. Then as @PiJei said just filter the list of the month by the excluded dates.

https://dotnetfiddle.net/2fv8bY

tstrand66
  • 968
  • 7
  • 11
0

I borrowed from PiJei's suggestion. Then extended that with method to get days in starting range, and with a method to build final ranges of the dates.

First, let's build the sample data: var month = EachDay(start, end);

var start1 = new DateTime(2018, 06, 04);
var end1 = new DateTime(2018, 06, 06);
var sub1 = EachDay(start1, end1);

var start2 = new DateTime(2018, 06, 11);
var end2 = new DateTime(2018, 06, 14);
var sub2 = EachDay(start2, end2);

var start3 = new DateTime(2018, 06, 17);
var end3 = new DateTime(2018, 06, 20);
var sub3 = EachDay(start3, end3);

To do that I'm using EachDay method which I borrowed from this answer.

public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

To get the final list which I call withoutExcluded I simply run this:

var withoutExcluded = month.Except(sub1).Except(sub2).Except(sub3);

Finally, I take this list and build ranges of the results.

var ranges = GetRanges(withoutExcluded);

To do that I created a simple helper Range class and GetRanges method that analyzes the dates and if they are consecutive dates builds a single range.

The helper Range class:

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

The helper GetRanges method:

public static IEnumerable<Range> GetRanges(IEnumerable<DateTime> dates)
{
    var start = dates.First();
    var end = dates.First();

    var prev = dates.First();
    var next = DateTime.Now;

    foreach(var d in dates.Skip(1))
    {
        next = d;
        var diff = next - prev;
        if (diff.TotalDays > 1)
        {
            yield return new Range { Start = start, End = end };
            start = d;   
        }

        prev = d;
        end = d;
    }

    yield return new Range { Start = start, End = end };
}

The final result looks like that:

range: 01/06/2018 00:00:00 03/06/2018 00:00:00
range: 07/06/2018 00:00:00 10/06/2018 00:00:00
range: 15/06/2018 00:00:00 16/06/2018 00:00:00
range: 21/06/2018 00:00:00 30/06/2018 00:00:00

I'll leave to you appropriate formatting of the results. But the logic is correct. In case you've lost track of what happens when below I'm pasting the whole solution:

class Program
{
    static void Main(string[] args)
    {
        var start = new DateTime(2018, 06, 01);
        var end = new DateTime(2018, 06, 30);

        var month = EachDay(start, end);

        var start1 = new DateTime(2018, 06, 04);
        var end1 = new DateTime(2018, 06, 06);
        var sub1 = EachDay(start1, end1);

        var start2 = new DateTime(2018, 06, 11);
        var end2 = new DateTime(2018, 06, 14);
        var sub2 = EachDay(start2, end2);

        var start3 = new DateTime(2018, 06, 17);
        var end3 = new DateTime(2018, 06, 20);
        var sub3 = EachDay(start3, end3);

        var withoutExcluded = month.Except(sub1).Except(sub2).Except(sub3);
        var ranges = GetRanges(withoutExcluded);

        foreach (var r in ranges)
        {
            Console.WriteLine($"range: {r.Start} {r.End}");
        }
    }

    //https://stackoverflow.com/a/1847601/3330348
    public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
    {
        for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
            yield return day;
    }

    public static IEnumerable<Range> GetRanges(IEnumerable<DateTime> dates)
    {
        var start = dates.First();
        var end = dates.First();

        var prev = dates.First();
        var next = DateTime.Now;

        foreach (var d in dates.Skip(1))
        {
            next = d;
            var diff = next - prev;
            if (diff.TotalDays > 1)
            {
                yield return new Range { Start = start, End = end };
                start = d;
            }

            prev = d;
            end = d;
        }

        yield return new Range { Start = start, End = end };
    }

    public class Range
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
    }
}
PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68