I've got my own way of doing this but I'm not convinced its the best, in C#
Given a List<DateTime>
, a DateTime startDate
and an DateTime endDate
. How would you return a new List<DateTime>
for every month between startDate
and endDate
that is not included within the original List<DateTime>
inclusive of the startDate
and endDate
.
Dates are not guarnteed to be the start of the month, could be any date within the month.
startDate
and endDate
could span multiple years.
The returned list should contain the first day of every month that is missing.
Thanks, and I hope it makes sense.
var list = new List<DateTime>
{
new DateTime(1231223423433132),
new DateTime(13223123132),
new DateTime(12333123132),
new DateTime(123345123132),
DateTime.Now,
new DateTime(5634534553)
};
var allYearMonthes = list.Select(o =>
Eumerable.Range(1, 12)
.Select(q => new { o.Year, Month = q }))
.SelectMany(o => o);
var enumerable = allYearMonthes.Except(list.Select(o => new { o.Year, o.Month }));
var dateTimes = enumerable.Select(o => new DateTime(o.Year, o.Month, 1));
EDIT: for those who interested in probably complete solution:
DateTime StartDate = DateTime.Now, EndDate = DateTime.Now.AddYears(5).AddMonths(2);
var allYearMonthes = Enumerable.Range(StartDate.Year, EndDate.Year - StartDate.Year -1)
.Select(o => Enumerable.Range(1, 12)
.Select(q => new { Year = o, Month = q }))
.SelectMany(o => o);
var enumerable = allYearMonthes.Except(list.Select(o => new { o.Year, o.Month }));
var dateTimes = enumerable.Select(o => new DateTime(o.Year, o.Month, 1));
Well, assuming the same month in different years is considered different:
private List<DateTime> GetUnincludedMonths(DateTime startDate, DateTime endDate,
IEnumerable<DateTime> dates)
{
var allMonths = new HashSet<Tuple<int, int>>(); //month, year
DateTime date = startDate;
while (date <= endDate)
{
allMonths.Add(Tuple.Create(date.Month, date.Year));
date = date.AddMonths(1);
}
allMonths.Add(Tuple.Create(endDate.Month, endDate.Year));
allMonths.ExceptWith(dates.Select(dt => Tuple.Create(dt.Month, dt.Year)));
return allMonths.Select(t => new DateTime(t.Item2, t.Item1, 1)).ToList();
}
Here's what I would do:
static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> currentDates, DateTime startDate, DateTime endDate)
{
var yearMonths = new HashSet<Tuple<int, int>>(currentDates.Select(d => Tuple.Create(d.Year, d.Month)));
DateTime current = new DateTime(startDate.Year, startDate.Month, 1);
if (current < startDate)
current = current.AddMonths(1);
while (current <= endDate)
{
if (!yearMonths.Contains(Tuple.Create(current.Year, current.Month)))
{
yield return current;
}
current = current.AddMonths(1);
}
}
EDIT: if you can't use Tuple, you can use an anonymous type instead, with a helper method to create the HashSet:
static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> currentDates, DateTime startDate, DateTime endDate)
{
var yearMonths = MakeHashSet(currentDates.Select(d => new { d.Year, d.Month }));
DateTime current = new DateTime(startDate.Year, startDate.Month, 1);
if (current < startDate)
current = current.AddMonths(1);
while (current <= endDate)
{
if (!yearMonths.Contains(new { current.Year, current.Month }))
{
yield return current;
}
current = current.AddMonths(1);
}
}
static HashSet<T> MakeHashSet<T>(IEnumerable<T> source)
{
return new HashSet<T>(source);
}
The MakeHashSet
method allows you to use type inference to create a HashSet<T>
when T is an anonymous type.
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