Let's say I have a List<DateTime> L
that contains holidays dates in the US. Let DateTime d
be today 2017/10/19. I want to calculate the next n-th non-holiday date (where n
is a positive integer let's say) consecutive to d
.
To do this, a concept of "iteration over all dates >= d
except dates belonging to L
" is involved. What is the best c# design to handle this ? (Performance is quite important.)
Remarks :
n
could be like 40-50 times "365"d+n
", or the list of non-holidays dates between two dates
It's really easy to create an infinite sequence of dates:
static IEnumerable<DateTime> GetDates(DateTime start)
{
DateTime current = start;
while (true)
{
yield return current;
current = current.AddDays(1);
}
}
Then you can use Except
with your holidays to get an infinite stream of dates except your holidays:
var nonHolidays = GetDates(DateTime.Today).Except(holidays);
To get the nth value (starting with 0 as the first) you can use ElementAt
:
var specificWorkDay = nonHolidays.ElementAt(n);
Of course if you're doing this a lot, you might want to generate "all the first X non-holiday dates" which is easy too:
var multipleWorkDays = nonHolidays.Take(x).ToList();
Note that even though GetDates
looks like it's in an infinite loop, that just means it will keep iterating forever if you keep asking the returned iterator for data (and until you hit DateTime.MaxValue
of course). It will only iterate as far as you ask it to. Just don't call GetDates().ToList()
or nonHolidays.ToList()
!
Suppose you have an IEnumerable<DateTime> GetHolidays(DateTime start)
method which returns a sequence of holidays in order, potentially infinite, from the given start date. (It's very important that the sequence is in order, as otherwise we can't tell reliably whether or not a date is a holiday.)
At that point, you need to be slightly smarter, as Except
is not going to work. It's still possible to do efficiently though:
static IEnumerable<DateTime> GetNonHolidays(DateTime start)
{
var holidays = GetHolidays(start);
var current = start;
foreach (var holiday in holidays)
{
// Yield everything until the next holiday
while (current < holiday)
{
yield return current;
current = current.AddDays(1);
}
// Skip this holiday, then look for the next one
current = current.AddDays(1);
}
// No more holidays? Now we can just yield infinitely...
while (true)
{
yield return current;
current = current.AddDays(1);
}
}
See more on this question at Stackoverflow