Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split date range into date range chunks

Tags:

I am looking for a method of splitting a date range into a series of date ranges by chunk size of days. I am planning on using this to buffer calls to a service which if the date range is too large, the service faults.

This is what I have come up with so far. It seems to work, but I am not sure if it will exit properly. This seems like something that has probably been done several times before, but I can't find it.

public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) {     var newStart = start;     var newEnd = start.AddDays(dayChunkSize);      while (true)     {         yield return new Tuple<DateTime, DateTime>(newStart, newEnd);          if (newEnd == end)             yield break;          newStart = newStart.AddDays(dayChunkSize);         newEnd = (newEnd.AddDays(dayChunkSize) > end ? end : newEnd.AddDays(dayChunkSize));     } } 

I'm looking for improvement suggestions, or "Dude, use this existing function for this!"

like image 860
Khan Avatar asked Dec 03 '12 19:12

Khan


2 Answers

I think your code fails when the difference between start and end is smaller than dayChunkSize. See this:

var singleRange = SplitDateRange(DateTime.Now, DateTime.Now.AddDays(7), dayChunkSize: 15).ToList(); Debug.Assert(singleRange.Count == 1); 

Proposed solution:

public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) {     DateTime chunkEnd;     while ((chunkEnd = start.AddDays(dayChunkSize)) < end)     {         yield return Tuple.Create(start, chunkEnd);         start = chunkEnd;     }     yield return Tuple.Create(start, end); } 
like image 145
Tomas Grosup Avatar answered Oct 07 '22 21:10

Tomas Grosup


There are a couple of problems with your solution:

  • the test newEnd == end may never be true, so the while could loop forever (I now see that this condition should always be triggered, but it wasn't obvious on first reading of the code; the while(true) feels a bit dangerous still)
  • AddDays is called three times for each iteration (minor performance issue)

Here is an alternative:

public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) {     DateTime startOfThisPeriod = start;     while (startOfThisPeriod < end)     {         DateTime endOfThisPeriod = startOfThisPeriod.AddDays(dayChunkSize);         endOfThisPeriod = endOfThisPeriod < end ? endOfThisPeriod : end;         yield return Tuple.Create(startOfThisPeriod, endOfThisPeriod);         startOfThisPeriod = endOfThisPeriod;     } } 

Note that this truncates the last period to end on end as given in the code in the question. If that's not needed, the second line of the while could be omitted, simplifying the method. Also, startOfThisPeriod isn't strictly necessary, but I felt that was clearer than reusing start.

like image 38
Matthew Strawbridge Avatar answered Oct 07 '22 21:10

Matthew Strawbridge