Find Missed Months

I have my own way of doing this, but I'm not sure if this is the best in C #

Given a List<DateTime> , a DateTime startDate and a DateTime endDate . How would you return a new List<DateTime> for each month between startDate and endDate , which is not included in the original List<DateTime> including startDate and endDate .

Dates are not guaranteed as the beginning of the month, can be any date within a month.

startDate and endDate can span several years.

The returned list should contain the first day of every month that is missing.

Thanks, and I hope this makes sense.

+6
c # datetime
source share
9 answers
 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 interested, probably the 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)); 
+3
source share

Well, assuming that on the same month in different years it 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(); } 
+3
source share

Here is what I will 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 cannot use Tuple, you can use an anonymous type using a helper method to create a 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.

+2
source share

LINQPad - solution for working:

 void Main() { var dates = new List<DateTime> { new DateTime(2011, 1, 1), new DateTime(2011, 3, 5), new DateTime(2011, 7, 28), }; var startDate = new DateTime(2011, 1, 1); var endDate = new DateTime(2012, 12, 31); var existingMonths = (from dt in dates select dt.Year * 12 + dt.Month - 1).Distinct().ToArray(); var missingMonths = from ym in Enumerable.Range( startDate.Year * 12 + startDate.Month - 1, (endDate.Year * 12 + endDate.Month) - (startDate.Year * 12 + startDate.Month) + 1) where !existingMonths.Contains(ym) select new DateTime(ym / 12, ym % 12 + 1, 1); missingMonths.Dump(); } 
+1
source share
 public IEnumerable<DateTime> GetMissingMonths( DateTime startDate, DateTime endDate, IEnumerable<DateTime> source) { IEnumerable<DateTime> sourceMonths = source.Select(x => new DateTime(x.Year, x.Month, 1)) .ToList() .Distinct(); return MonthsBetweenInclusive(startDate, endDate).Except(sourceMonths); } public IEnumerable<DateTime> MonthsBetweenInclusive( DateTime startDate, DateTime endDate) { DateTime currentMonth = new DateTime(startDate.Year, startDate.Month, 1); DateTime endMonth = new DateTime(endDate.Year, endDate.Month, 1); while(currentMonth <= endMonth) { yield return currentMonth; currentMonth = currentMonth.AddMonths(1); } } 
+1
source share
 static void Main(string[] args) { var days = (new string[] { "3/23/2000", "7/3/2004", "1/3/2004", "3/1/2011" }) .Select(a => Convert.ToDateTime(a)); days = days.Select(a => a.AddDays(1 - (a.Day))).Distinct(); days = days.OrderBy(a => a); var missingMonths = GetMissingMonths(days).ToList(); } private static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> days) { DateTime previous = days.First(); foreach (var current in days.Skip(1)) { int months = (current.Month - previous.Month) + 12 * (current.Year - previous.Year); for (int i = 1; i < months; i++) { yield return previous.AddMonths(i); } previous = current; } } 
0
source share

Thanks to Jani +1 for his idea. This is one line of code :)

 void Main() { var list = new List<DateTime> { new DateTime(2005, 10, 11), new DateTime(2009, 3, 4), new DateTime(2010, 5, 8), new DateTime(2010, 8, 10), DateTime.Now, new DateTime(2010, 4, 8) }; var result= Enumerable.Range(list.Min (l => l.Year), list.Max (l => l.Year) - list.Min (l => l.Year)). SelectMany (e => Enumerable.Range(1, 12).Select (en => new DateTime(e, en, 1))). Except(list.Select(o => new DateTime(o.Year, o.Month, 1))). Where (o => o.Date > list.Min (l => l.Date) && o.Date < list.Max (l => new DateTime(l.Year, l.Month, 1))); } 
0
source share

I will give up my hat because it is fun. and I didn’t see anyone putting DateTime in a normal loop that I never need to do, so again ... fun.

 IEnumerable<DateTime> FindMissingMonths(DateTime startDate, DateTime endDate, IEnumerable<DateTime> inputs) { var allMonths = new List<DateTime>(); for (DateTime d = startDate; d < endDate; d = d.AddMonths(1)) { allMonths.Add(new DateTime(d.Year, d.Month, 1)); } var usedMonths = (from d in inputs select new DateTime(d.Year, d.Month, 1)).Distinct(); return allMonths.Except(usedMonths); } 

Bug fixed, tested, working.

0
source share
  public IList<DateTime> GetMissingMonths(IList<DateTime> currentList, DateTime startDate, DateTime endDate) { // Create a list for the missing months IList<DateTime> missingList = new List<DateTime>(); // Select a startdate DateTime testingDate = startDate; // Begin by the month of startDate and ends with the month of endDate // month of startDate and endDate included while(testingDate <= endDate) { if (currentList.Count(m => m.Month == testingDate.Month && m.Year == testingDate.Year) == 0) { missingList.Add(new DateTime(testingDate.Year, testingDate.Month, 1)); } testingDate = testingDate.AddMonths(1); } return missingList; } 
-one
source share

All Articles