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!"
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); }
There are a couple of problems with your solution:
newEnd == end
may never be true, so the while
could loop foreverwhile(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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With