I'm trying to find missing dates between two DateTime variables for a collection of DateTimes.
For example.
Collection
2010-01-01
2010-01-02
2010-01-03
2010-01-05
DateRange
2010-01-01 -> 2010-01-06
would give me a List<DateTime> of
2010-01-04
2010-01-06
I can think of a few was of implementing this but nothing clean and simple
Any ideas?
I can think of a lot of ways of implementing this, e.g.:
DateTime[] col = { new DateTime(2010, 1, 1),
new DateTime(2010, 1, 2),
new DateTime(2010, 1, 3),
new DateTime(2010, 1, 5)};
var start = new DateTime(2010, 1, 1);
var end = new DateTime(2010, 1, 6);
var range = Enumerable.Range(0, (int)(end - start).TotalDays + 1)
.Select(i => start.AddDays(i));
var missing = range.Except(col);
And you could put the range-stuff into an Extension-Method
public static class extensions
{
public static IEnumerable<DateTime> Range(this DateTime startDate, DateTime endDate)
{
return Enumerable.Range(0, (int)(endDate - startDate).TotalDays + 1)
.Select(i => startDate.AddDays(i));
}
}
Then it would be simply
DateTime[] col = { new DateTime(2010, 1, 1),
new DateTime(2010, 1, 2),
new DateTime(2010, 1, 3),
new DateTime(2010, 1, 5)};
var start = new DateTime(2010, 1, 1);
var end = new DateTime(2010, 1, 6);
var missing = start.Range(end).Except(col);
But maybe this is not a high-performance-solution :-)
Depending on exactly what you are looking for and the sizes of the sets of data. A simple way would be to load the dates into a collection, then use a simple loop. I'll add a code sample here in a second.
DateTime currentDate = new DateTime(2010, 1, 1);
DateTime endDate = new DateTime(2010, 1, 6);
List<DateTime> existingDates = new List<DateTime>; //You fill with values
List<DateTime> missingDates = new List<DateTime>;
while(currentDate <= endDate)
{
if(existingDates.contains(currentDate))
missingDates.Add(currentDate);
//Increment date
currentDate = currentDate.AddDays(1);
}
Using this example you just need to load "existingDates" with the proper values, then the "missingDates" list will have your results
In .NET 2.0 :)
static void Main(string[] args)
{
List<DateTime> dates = new List<DateTime>();
dates.Add(new DateTime(2010, 01, 27));
dates.Add(new DateTime(2010, 01, 30));
dates.Add(new DateTime(2010, 01, 31));
dates.Add(new DateTime(2010, 02, 01));
DateTime startDate = new DateTime(2010, 01, 25);
DateTime endDate = new DateTime(2010, 02, 02);
List<DateTime> missingDates = new List<DateTime>(GetMissingDates(dates, startDate, endDate));
}
private static IEnumerable<DateTime> GetMissingDates(IList<DateTime> dates, DateTime startDate, DateTime endDate)
{
TimeSpan _timeStamp = endDate - startDate;
DateTime _tempDateTime = startDate;
IList<DateTime> _dateTimeRange = new List<DateTime>();
IList<DateTime> _missingDates = new List<DateTime>();
for (int i = 0; i <= _timeStamp.Days; i++)
{
_dateTimeRange.Add(_tempDateTime);
_tempDateTime = _tempDateTime.AddDays(1);
}
foreach (DateTime dt in _dateTimeRange)
{
if (!dates.Contains(dt))
yield return dt;
}
}
Lazy evaluated helper method aids in generating the list of dates to compare with. Might want to performance profile this method for large collections.
void Main()
{
var dates = new[] {new DateTime(2000,1,1), new DateTime(2000,1,5)};
DateHelper.Range(new DateTime(2000,1,1), new DateTime(2000,1,5)).Except(dates).Dump();
}
// Define other methods and classes here
public static class DateHelper {
public static IEnumerable<DateTime> Range(DateTime start, DateTime end) {
var days = end.Subtract(start).Days;
var next = start;
for(var i = 0; i<days; i++) {
next = next.AddDays(1);
yield return next;
}
}
}
var dates = new List<DateTime>
{
new DateTime( 2010, 01, 01 ),
new DateTime( 2010, 01, 02 ),
new DateTime( 2010, 01, 03 ),
new DateTime( 2010, 01, 05 )
};
var targetDate = new DateTime( 2010, 01, 01 );
var missingDates = new List<DateTime>();
while ( targetDate <= new DateTime( 2010, 01, 06 ) )
{
if ( !dates.Contains( targetDate ) )
missingDates.Add( targetDate );
targetDate = targetDate.AddDays( 1 );
}
foreach ( var date in missingDates )
Debug.WriteLine( date.ToString() );
If you were thinking of solving this is LINQ, I do not believe it is possible unless you also had a list of all dates between the min and max date. In SQL, this amounts to a calendar table that contains all dates across a given time period.
Here is a LINQ solution where I create the Calendar list I mentioned above and then query for missing dates:
var dates = new List<DateTime>
{
new DateTime( 2010, 01, 01 ),
new DateTime( 2010, 01, 02 ),
new DateTime( 2010, 01, 03 ),
new DateTime( 2010, 01, 05 )
};
var calendar = new List<DateTime>();
var targetDate = new DateTime( 2010, 01, 01 );
while ( targetDate <= new DateTime( 2010, 01, 06 ) )
{
calendar.Add( targetDate );
targetDate = targetDate.AddDays( 1 );
}
var missingDates = ( from date in calendar
where !dates.Contains( date )
select date ).ToList();
foreach ( var date in missingDates )
Debug.WriteLine( date.ToString() );
Related
I want to generate a list of weekly business dates between two dates excluding weekends and holidays. I have managed to exclude the Weekends and created a routine for the holidays. Now I need to exclude the holidays. Below is my code.
using System;
using System.Collections.Generic;
using System.Linq;
namespace SeriesTest
{
class Program
{
public class BusinessWeekDays
{
public DateTime Monday;
public DateTime Sunday;
}
private static List<DateTime> Holidays = new List<DateTime>()
{
new DateTime(1, 1, 1), //New Year Day
new DateTime(1, 5, 1), //Labour Day
new DateTime(1, 7, 4), //Independence Day
new DateTime(1, 3, 1), //Martin Luther King Jr. Day
new DateTime(1, 3, 2), //Presidents Day
new DateTime(1, 12, 25), //Christmas
new DateTime(1, 5, 5), //Memorial Day
new DateTime(1, 9, 1), //Labor Day
new DateTime(1, 10, 2), //Columbus Day
new DateTime(1, 11, 4), //Columbus Day
};
private static bool IsHoliday(DateTime value, List<DateTime> holidays = null)
{
if (null == holidays)
holidays = Holidays;
return (value.DayOfWeek == DayOfWeek.Sunday) ||
(value.DayOfWeek == DayOfWeek.Saturday) ||
holidays.Any(holiday => holiday.Day == value.Day &&
holiday.Month == value.Month);
}
public static int BusinessDays(DateTime fromDate, DateTime toDate, List<DateTime> holidays = null)
{
int result = 0;
for (var date = fromDate;
date < toDate.Date;
date = date.AddDays(1))
if (!IsHoliday(date, holidays))
result += 1;
return result;
}
static void Main(string[] args)
{
var StartDate = DateTime.Parse("02/12/2019");
var SeriesEndDate = DateTime.Parse("12/31/2025");
var holidays = new List<DateTime>();
var firstMonday = Enumerable.Range(0, 7)
.SkipWhile(x => StartDate.AddDays(x).DayOfWeek != DayOfWeek.Monday)
.Select(x => StartDate.AddDays(x))
.First();
var ts = (SeriesEndDate - firstMonday);
var dates = new List<BusinessWeekDays>();
for (var i = 0; i < ts.Days; i += 7)
{
//Remove holidays. Weekend already removed here
if (BusinessDays(StartDate, SeriesEndDate, holidays) != 0)
{
dates.Add(new BusinessWeekDays { Monday = firstMonday.AddDays(i), Sunday = firstMonday.AddDays(i + 9) });
}
}
Console.WriteLine(dates);
}
}
}
It looks like you already have everything you need, but holidays is never set to your Holidays. Change the line below so that holidays is set to null. Then it will hit the null check in IsHoliday and be set properly.
var holidays = new List<DateTime>();
Should be:
var holidays = null;
How can i get for example 4th last working day (which is not a holiday) from a month ?
In August its 26.8.2015 day (Wednesday)
In September its 25.9.2015 day (Friday)
My actual code is following (i get an inspiration from here)
but i have an recursion and also it has syntax error.
static bool getLastXthWorkingDay(int x)
{
var weekends = new DayOfWeek[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
int month = DateTime.Now.Month;
int year = DateTime.Now.Year;
IFormatProvider culture = new System.Globalization.CultureInfo("cs-CZ", true);
var holidays = new DateTime[] {
Convert.ToDateTime(String.Format("1.1.{0}",year),culture),
Convert.ToDateTime(String.Format("6.4.{0}",year),culture),
Convert.ToDateTime(String.Format("1.5.{0}",year),culture),
Convert.ToDateTime(String.Format("8.5.{0}",year),culture),
Convert.ToDateTime(String.Format("5.7.{0}",year),culture),
Convert.ToDateTime(String.Format("6.7.{0}",year),culture),
Convert.ToDateTime(String.Format("28.9.{0}",year),culture),
Convert.ToDateTime(String.Format("28.10.{0}",year),culture),
Convert.ToDateTime(String.Format("17.11.{0}",year),culture),
Convert.ToDateTime(String.Format("24.12.{0}",year),culture),
Convert.ToDateTime(String.Format("25.12.{0}",year),culture),
Convert.ToDateTime(String.Format("26.12.{0}",year),culture)
};
//Fetch the amount of days in your given month.
int daysInMonth = DateTime.DaysInMonth(year, month);
//Here we create an enumerable from 1 to daysInMonth,
//and ask whether the DateTime object we create belongs to a weekend day,
//if it doesn't, add it to our IEnumerable<int> collection of days.
IEnumerable<int> businessDaysInMonth = Enumerable.Range(1, daysInMonth)
.Where(d => !weekends.Contains(new DateTime(year, month, d).DayOfWeek));
var lastXthWorkingDay = businessDaysInMonth.Skip(Math.Max(0, businessDaysInMonth.Count() - x));
// This code has syntax error ") expected" but i dont see where ?
//if holidays.Contains(Convert.ToDateTime(String.Format("{0},{1},{2}", lastXthWorkingDay, month, year),culture))
//{
// getLastXthWorkingDay(x-1);
//}
//else { return true; }
}
any idea ?
Ok i think i got it. Thank you all
static int GetLastXthWorkingDay(int year, int month, int x)
{
var weekends = new DayOfWeek[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
var holidays = new DateTime[] {
new DateTime(year, 4, 6),
new DateTime(year, 5, 1),
new DateTime(year, 5, 8),
new DateTime(year, 7, 5),
new DateTime(year, 7, 6),
new DateTime(year, 9, 28),
new DateTime(year, 10, 28),
new DateTime(year, 11, 17),
new DateTime(year, 12, 24),
new DateTime(year, 12, 25),
new DateTime(year, 12, 26),
};
//Fetch the amount of days in your given month.
int daysInMonth = DateTime.DaysInMonth(year, month);
//Here we create an enumerable from 1 to daysInMonth,
//and ask whether the DateTime object we create belongs to a weekend day,
//if it doesn't, add it to our IEnumerable<int> collection of days.
IEnumerable<int> businessDaysInMonth = Enumerable.Range(1, daysInMonth)
.Where(d => !weekends.Contains(new DateTime(year, month, d).DayOfWeek));
var lastXthWorkingDay = businessDaysInMonth.Skip(Math.Max(0, businessDaysInMonth.Count() - x));
foreach (var day in lastXthWorkingDay)
{
if ( holidays.Contains(new DateTime(year, month, day)) )
{
return GetLastXthWorkingDay(year, month, x + 1);
//return day-1;
}
else { return day; }
}
return 0;
}
I have two datetimepickers. So I want to get the days of the week between the selected values.
Example: date1 = 14/1/2015 and date2 = 17/1/2015
So the result should be: days = Wed. Thu. Fri. Sat.
You want to get the DayOfWeeks of all DateTimes between two dates?
int daysDiff = (date2 - date1).Days;
List<DayOfWeek> days = Enumerable.Range(0, daysDiff + 1) // +1 because you want to include start and end date
.Select(d => date1.AddDays(d).DayOfWeek)
.ToList();
You need to add using System.Linq;
You can easily iterate your DateTime values like;
var dt1 = new DateTime(2015, 1, 14);
var dt2 = new DateTime(2015, 1, 17);
while (dt2 >= dt1)
{
Console.WriteLine(dt1.DayOfWeek);
dt1 = dt1.AddDays(1);
}
Result will be;
Wednesday
Thursday
Friday
Saturday
If you wanna their abbreviated day names as Wed, Thu, Fri, Sat, you can use "ddd" custom format specifier with a english-based culture (like InvariantCulture) like;
var dt1 = new DateTime(2015, 1, 14);
var dt2 = new DateTime(2015, 1, 17);
while (dt2 >= dt1)
{
Console.WriteLine(dt1.ToString("ddd", CultureInfo.InvariantCulture));
dt1 = dt1.AddDays(1);
}
Another alternative, which includes non-usual use of a for-loop and the yield return statement:
void Main()
{
var daysOfWeek = DaysBetween(
new DateTime(2015, 1, 14),
new DateTime(2015, 1, 17));
Console.WriteLine(
String.Join(", ", daysOfWeek.Select(d => d.ToString().Substring(0, 3))));
// prints: Wed, Thu, Fri, Sat
}
IEnumerable<DayOfWeek> DaysBetween(DateTime start, DateTime end)
{
for (var dateTime = start; dateTime <= end; dateTime = dateTime.AddDays(1))
{
yield return dateTime.DayOfWeek;
}
}
How to get All Days between given two Dates, given "Day Name"?
Ex: Start_Date = Jan 1, 2011
End_Date = Jan 20, 2011
Day Name = Sunday
Here we need to get all Dates with dayname as "SUNDAY"
Ex:
Jan 02, 2011
Jan 09, 2011
Jan 16, 2011
I like to use Enumerable.Range for tasks like that:
DateTime start = new DateTime(2011,1,1);
DateTime end = new DateTime(2011,1,20);
var datesThatAreSundays = Enumerable
.Range(start.DayOfYear, end.Subtract(start).Days + 1)
.Select(n => start.AddDays(n - start.DayOfYear))
.Where(d => d.DayOfWeek == DayOfWeek.Sunday);
DateTime startDate = new DateTime(2011, 1, 1);
DateTime endDate = new DateTime(2011, 1, 20);
while (startDate < endDate)
{
if (startDate.DayOfWeek == DayOfWeek.Sunday)
{
// Do something
}
startDate = startDate.AddDays(1);
}
How about something like
DateTime startDate = new DateTime(2011, 01, 01);
DateTime endDate = new DateTime(2011, 01, 20);
string dayName = "sunday";
List<DateTime> list = new List<DateTime>();
for (DateTime runDate = startDate; runDate <= endDate; runDate = runDate.AddDays(1))
{
if (runDate.DayOfWeek.ToString().ToLower() == dayName)
list.Add(runDate);
}
or even using Enum.TryParse
DateTime startDate = new DateTime(2011, 01, 01);
DateTime endDate = new DateTime(2011, 01, 20);
string dayName = "sunday";
DayOfWeek dow;
Enum.TryParse(dayName, true, out dow);
List<DateTime> list = new List<DateTime>();
for (DateTime runDate = startDate; runDate <= endDate; runDate = runDate.AddDays(1))
{
if (runDate.DayOfWeek == dow)
list.Add(runDate);
}
Without having tested:
DateTime start = x;
DateTime end = y;
while (start.DayOfWeek != z)
start = start.AddDays(1);
while (start <= end)
{
//DoStuff
start = start.AddDays(7);
}
You may try this
var firstDate = new DateTime(2011, 1, 1);
var lastDate = new DateTime(2011, 1, 20);
var result = (from el in Enumerable.Range(0, (lastDate - firstDate).Days)
let date = firstDate.AddDays(el)
where date.DayOfWeek == DayOfWeek.Sunday
select date).ToArray();
I have two date time ranges 8/27/2011 and 8/31/2011 how i can make get all days ? like that : 8/28/2011, 8/29/2011 and 8/30/2011
thanks
Here is a code snippet to get all days between a start and end date inclusive:
DateTime today = new DateTime(2011, 8, 29);
DateTime nextWeek = new DateTime(2011, 9, 4);
TimeSpan difference = nextWeek - today;
List<DateTime> days = new List<DateTime>();
for (int i = 0; i <= difference.Days; i++)
{
days.Add(today.AddDays(i));
}
foreach (var dateTime in days)
{
Console.WriteLine(dateTime);
}
Console.ReadLine();
Output:
8/29/2011 12:00:00 AM
8/30/2011 12:00:00 AM
8/31/2011 12:00:00 AM
9/1/2011 12:00:00 AM
9/2/2011 12:00:00 AM
9/3/2011 12:00:00 AM
9/4/2011 12:00:00 AM
To piggy back off of davecoulter, if you need to do this all over your application for your DateTime objects, you might want to define an extenion method for your DateTime object.
void Main()
{
DateTime today = new DateTime(2011, 8, 29);
DateTime nextWeek = new DateTime(2011, 9, 4);
foreach (DateTime dateTime in today.ListAllDates(nextWeek))
{
Console.WriteLine(dateTime);
}
Console.ReadLine();
}
public static class DateTimeExtenions
{
public static IEnumerable<DateTime> ListAllDates(this DateTime lhs, DateTime futureDate)
{
List<DateTime> dateRange = new List<DateTime>();
TimeSpan difference = (futureDate - lhs);
for(int i = 0; i <= difference.Days; i++)
{
dateRange.Add(lhs.AddDays(i));
}
return dateRange;
}
}
You can copy this straight into LinqPad and run as program to test it out.
using System;
using System.Linq;
var startDate = new DateTime(2011, 9, 1);
var days = Enumerable.Range(0, 10).Select(n => startDate.AddDays(n));
Create a new date from both datetimes datetime to make sure that it they are at the start of the day. Then run a for loop that works from starttime.Ticks to endtime.Ticks and increments by TimeSpan.TicksPerDay and create a new DateTime that you add to a list for every value. The example below will not include the end date but you can easily fix that.
var start= new DateTime(2009,01,01).Ticks;
var end= new DateTime(2009,01,10).Ticks;
List<DateTime> dates = new List<DateTime>();
for (var i = start; i < end; i+=TimeSpan.TicksPerDay) {
dates.Add(new DateTime(i));
}
Or you could loop through between them and call the AddDays method.