Related
I have list of Appointment class.
In below code;
appointment1,appointment2 and appointment3 are intersect.
appointment4 and appointment5 are intersect.
appointment6 is non intersect
appointment1,appointment2 and appointment3 start datetime is '2018-07-10 08:00:00' and finish datetime is '2018-07-10 12:00:00' and total time is 4 hours here.
appointment4 and appointment5 start datetime is '2018-07-10 14:00:00' and finishdatetime is '2018-07-10 17:00:00' and total time is 3 hours here.
and appointment6 is non intersect comes 1 hour here.
And total time is 4+3+1=8,
How can i find 8 in given Appointment datetime values.
class Program
{
static void Main(string[] args)
{
List<Appointment> appointments = new List<Appointment>();
Appointment appointment1 = new Appointment();
appointment1.StartDate = new DateTime(2018, 07, 11, 08, 00, 00);
appointment1.FinishDate = new DateTime(2018, 07, 11, 11, 00, 00);
Appointment appointment2 = new Appointment();
appointment2.StartDate = new DateTime(2018, 07, 11, 10, 00, 00);
appointment2.FinishDate = new DateTime(2018, 07, 11, 12, 00, 00);
Appointment appointment3 = new Appointment();
appointment3.StartDate = new DateTime(2018, 07, 11, 09, 00, 00);
appointment3.FinishDate = new DateTime(2018, 07, 11, 12, 00, 00);
Appointment appointment4 = new Appointment();
appointment4.StartDate = new DateTime(2018, 07, 11, 14, 00, 00);
appointment4.FinishDate = new DateTime(2018, 07, 11, 16, 00, 00);
Appointment appointment5 = new Appointment();
appointment5.StartDate = new DateTime(2018, 07, 11, 15, 00, 00);
appointment5.FinishDate = new DateTime(2018, 07, 11, 17, 00, 00);
Appointment appointment6 = new Appointment();
appointment6.StartDate = new DateTime(2018, 07, 11, 18, 00, 00);
appointment6.FinishDate = new DateTime(2018, 07, 11, 19, 00, 00);
appointments.Add(appointment1);
appointments.Add(appointment2);
appointments.Add(appointment3);
appointments.Add(appointment4);
appointments.Add(appointment5);
appointments.Add(appointment6);
Console.ReadLine();
}
}
public class Appointment
{
public DateTime StartDate { get; set; }
public DateTime FinishDate { get; set; }
}
You first need to merge the overlapping times and then sum the timespan:
void Main()
{
List<Appointment> appointments = new List<Appointment>();
Appointment appointment1 = new Appointment();
appointment1.StartDate = new DateTime(2018, 07, 11, 08, 00, 00);
appointment1.FinishDate = new DateTime(2018, 07, 11, 11, 00, 00);
Appointment appointment2 = new Appointment();
appointment2.StartDate = new DateTime(2018, 07, 11, 10, 00, 00);
appointment2.FinishDate = new DateTime(2018, 07, 11, 12, 00, 00);
Appointment appointment3 = new Appointment();
appointment3.StartDate = new DateTime(2018, 07, 11, 09, 00, 00);
appointment3.FinishDate = new DateTime(2018, 07, 11, 12, 00, 00);
Appointment appointment4 = new Appointment();
appointment4.StartDate = new DateTime(2018, 07, 11, 14, 00, 00);
appointment4.FinishDate = new DateTime(2018, 07, 11, 16, 00, 00);
Appointment appointment5 = new Appointment();
appointment5.StartDate = new DateTime(2018, 07, 11, 15, 00, 00);
appointment5.FinishDate = new DateTime(2018, 07, 11, 17, 00, 00);
Appointment appointment6 = new Appointment();
appointment6.StartDate = new DateTime(2018, 07, 11, 18, 00, 00);
appointment6.FinishDate = new DateTime(2018, 07, 11, 19, 00, 00);
appointments.Add(appointment1);
appointments.Add(appointment2);
appointments.Add(appointment3);
appointments.Add(appointment4);
appointments.Add(appointment5);
appointments.Add(appointment6);
var ranges = appointments.Select(a => new Range {Start=a.StartDate, End=a.FinishDate});
var total = MergeTimes(ranges).Sum(a => (a.End-a.Start).TotalHours);
Console.WriteLine(total);
}
public class Appointment
{
public DateTime StartDate { get; set; }
public DateTime FinishDate { get; set; }
}
public class Range
{
public DateTime Start {get;set;}
public DateTime End {get;set;}
}
public IEnumerable<Range> MergeTimes(IEnumerable<Range> times)
{
if (times.Count() == 0)
{
return times;
}
Range[] orderedTimes = (from t in times
orderby t.Start
select t).ToArray();
List<Range> merged = new List<Range>();
Range current = new Range
{
Start = orderedTimes[0].Start,
End = orderedTimes[0].End
};
for (int i = 0; i < orderedTimes.Length; i++)
{
if (current.Start <= orderedTimes[i].End && current.End >= orderedTimes[i].Start)
{
current.Start = ((current.Start < orderedTimes[i].Start) ? current.Start : orderedTimes[i].Start);
current.End = ((current.End > orderedTimes[i].End) ? current.End : orderedTimes[i].End);
}
else
{
merged.Add(new Range
{
Start = current.Start,
End = current.End
});
current = new Range
{
Start = orderedTimes[i].Start,
End = orderedTimes[i].End
};
}
}
merged.Add(new Range
{
Start = current.Start,
End = current.End
});
return merged;
}
Let's sort the appointments and then Aggregate them: we have only 3 choices to implement:
Appointments disjoint
Appointment includes the next appointment
Appointments overlap
Sample code:
var total = appointments
.OrderBy(appointment => appointment.StartDate)
.Aggregate(new Tuple<double, DateTime?>(0.0, null), (acc, item) => {
if (!acc.Item2.HasValue || acc.Item2.Value <= item.StartDate) // Disjoint
return new Tuple<double, DateTime?>(
acc.Item1 + (item.FinishDate - item.StartDate).TotalHours,
item.FinishDate);
else if (acc.Item2.Value >= item.FinishDate) // Include
return acc;
else // Partially overlap
return new Tuple<double, DateTime?>(
acc.Item1 + (item.FinishDate - acc.Item2.Value).TotalHours,
item.FinishDate);
})
.Item1;
// 8
Console.WriteLine(total);
Are you aware of the properties on the TimeSpan class?
(dateA - dateB).TotalMinutes
https://msdn.microsoft.com/en-us/library/system.timespan.totalminutes.aspx
One way to solve this would be to gather the hours that are covered by appointments, then group them.
You could add a method to the Appointment class to get the hours covered by the appointment:
public IEnumerable<int> GetHours()
{
List<int> hours = new List<int>();
var startDate = StartDate;
var finishDate = FinishDate;
while(startDate < finishDate)
{
hours.Add(startDate.Hour);
startDate = startDate.AddHours(1);
}
return hours;
}
You can then group them:
var result = appointments.SelectMany(a => a.GetHours()).GroupBy(i => i);
Console.WriteLine("Total hours: {0}", result.Count()); //This is the count
foreach (var hour in result)
{
Console.WriteLine("{0} => {1}", hour.Key, hour.Count());
}
Output in console:
Total hours: 8
8 => 1
9 => 2
10 => 3
11 => 2
14 => 1
15 => 2
16 => 1
18 => 1
I was hoping for some help with something I have been working on.
I have an ASP.NET web form that is trying to be a calculator, now I have dates and days of the week sorted as well as other forms of other parts working.
Now I need to add public holidays to this form.
I have an array holding the dates and if I put a date range of any type I get the full count as if it was counting for a full year.
And the intent is to read the date rage and pick the holidays in said date rage and only show said dates e.g.over two years the count should be double.
I'm sure it's something simple, I'm just not seeing it.
public class Program
{
public static void Main()
{
DisplayDayInfo(new DateTime(2018, 01, 01), new DateTime(2019, 01, 01));
// Show info for April 2018
//DisplayDayInfo(new DateTime(2018, 04, 01), new DateTime(2018, 05, 01));
}
private static int GetDayCount(IEnumerable<DateTime>dates, DayOfWeek dayOfWeek)
{
return dates == null ? 0 : dates.Count(date => date.DayOfWeek == dayOfWeek);
}
private static int GetHoliDayCount(IEnumerable<DateTime> holiday, DayOfWeek dayOfWeek)
{
DateTime[] holidayState = new DateTime[]
{
new DateTime(DateTime.Now.Year, 1, 1),
new DateTime(DateTime.Now.Year, 1, 26),
new DateTime(DateTime.Now.Year, 3, 30),
new DateTime(DateTime.Now.Year, 3, 31),
new DateTime(DateTime.Now.Year, 4, 1),
new DateTime(DateTime.Now.Year, 4, 2),
new DateTime(DateTime.Now.Year, 4, 25),
new DateTime(DateTime.Now.Year, 5, 7),
new DateTime(DateTime.Now.Year, 8, 15),
new DateTime(DateTime.Now.Year, 9, 1),
new DateTime(DateTime.Now.Year, 12, 25),
new DateTime(DateTime.Now.Year, 12, 26)
};
return holiday == null ? 0 : holidayState.Count(date => date.DayOfWeek == dayOfWeek);
}
private static void DisplayDayInfo(DateTime fromDate, DateTime toDate)
{
var numDays = toDate.Subtract(fromDate).Days;
Console.WriteLine("Total number of days from {0} to {1}: {2}\n",fromDate.ToShortDateString(), toDate.ToShortDateString(), numDays);
var dates = Enumerable.Range(0, numDays).Select(days => fromDate.AddDays(days)).ToArray();
var holiday = Enumerable.Range(0, numDays).Select(days => fromDate.AddDays(days)).ToArray();
foreach (DayOfWeek dayOfWeek in Enum.GetValues(typeof(DayOfWeek)))
{
Console.WriteLine("{0} {1} ", dayOfWeek, GetDayCount(dates, dayOfWeek));
}
Console.WriteLine("****************************************************");
foreach (DayOfWeek dayOfWeek in Enum.GetValues(typeof(DayOfWeek)))
{
Console.WriteLine("{0} {1} ", dayOfWeek, GetHoliDayCount(holiday, dayOfWeek));
}
}
}
https://dotnetfiddle.net/XjoCpf
Any help would be grate, Thank you.
I changed some implementation of your methods based on your intent:
private static DateTime? GetHoliDayCount(DateTime holiday)
{
DateTime? holidayToReturn = null ;
DateTime[] holidayState = new DateTime[]
{
new DateTime(DateTime.Now.Year, 1, 1),
new DateTime(DateTime.Now.Year, 1, 26),
new DateTime(DateTime.Now.Year, 3, 30),
new DateTime(DateTime.Now.Year, 3, 31),
new DateTime(DateTime.Now.Year, 4, 1),
new DateTime(DateTime.Now.Year, 4, 2),
new DateTime(DateTime.Now.Year, 4, 25),
new DateTime(DateTime.Now.Year, 5, 7),
new DateTime(DateTime.Now.Year, 8, 15),
new DateTime(DateTime.Now.Year, 9, 1),
new DateTime(DateTime.Now.Year, 12, 25),
new DateTime(DateTime.Now.Year, 12, 26)
};
if (holidayState.Contains(holiday))
{
holidayToReturn = holiday;
}
return holidayToReturn;
}
Then you need to modify your DisplayDateInfo to loop over the holiday enumerable.
private static void DisplayDayInfo(DateTime fromDate, DateTime toDate)
{
var numDays = toDate.Subtract(fromDate).Days;
Console.WriteLine("Total number of days from {0} to {1}: {2}\n", fromDate.ToShortDateString(), toDate.ToShortDateString(), numDays);
var dates = Enumerable.Range(0, numDays).Select(days => fromDate.AddDays(days)).ToArray();
var holiday = Enumerable.Range(0, numDays).Select(days => fromDate.AddDays(days)).ToArray();
foreach (DayOfWeek dayOfWeek in Enum.GetValues(typeof(DayOfWeek)))
{
Console.WriteLine("{0} {1} ", dayOfWeek, GetDayCount(dates, dayOfWeek));
}
Console.WriteLine("****************************************************");
foreach (DateTime dayOfWeek in holiday)
{
string holidayToWrite = GetHoliDayCount(dayOfWeek).ToString();
if (holidayToWrite != "")
Console.WriteLine("{0} ", holidayToWrite);
}
}
Result:
Or if you don't want to edit the method signature of your GetHolidayCount method and do no changes in your DisplayDateInfo method, you can do this by comparing your IEnumerable range to the dates in the array, and if an item in the array contains the date in your IEnumerable then add it to the final list.:
private static int GetHoliDayCount(IEnumerable<DateTime> holiday, DayOfWeek dayOfWeek)
{
List<DateTime> days = holiday.ToList(); //convert this to list
List<DateTime> daysToAdd = new List<DateTime>(); //have a list as container for the holidays after the comparison.
DateTime[] holidayState = new DateTime[]
{
new DateTime(DateTime.Now.Year, 1, 1),
new DateTime(DateTime.Now.Year, 1, 26),
new DateTime(DateTime.Now.Year, 3, 30),
new DateTime(DateTime.Now.Year, 3, 31),
new DateTime(DateTime.Now.Year, 4, 1),
new DateTime(DateTime.Now.Year, 4, 2),
new DateTime(DateTime.Now.Year, 4, 25),
new DateTime(DateTime.Now.Year, 5, 7),
new DateTime(DateTime.Now.Year, 8, 15),
new DateTime(DateTime.Now.Year, 9, 1),
new DateTime(DateTime.Now.Year, 12, 25),
new DateTime(DateTime.Now.Year, 12, 26)
};
foreach (DateTime day in days)
{
if (holidayState.Contains(day))
{
daysToAdd.Add(day);
}
}
holiday = daysToAdd;
return holiday == null ? 0 : holidayState.Count(date => date.DayOfWeek == dayOfWeek);
}
You can also use this method if you don't want the instantiation of your holidayState array to be limited to the current year. What this does is that it groups the years in your enumerable and runs the comparison of your holiday enumerable to the array for each year in the grouping:
private static int GetHoliDayCount(IEnumerable<DateTime> holiday, DayOfWeek dayOfWeek)
{
var yearsGroup = holiday.GroupBy(x => x.Year);
List<DateTime> daysToAdd = new List<DateTime>();
DateTime[] holidayState = null;
foreach (var year in yearsGroup)
{
List<DateTime> days = holiday.Where(x => x.Year == Convert.ToInt32(year.Key)).ToList();
holidayState = new DateTime[]
{
new DateTime(Convert.ToInt32(year.Key), 1, 1),
new DateTime(Convert.ToInt32(year.Key), 1, 26),
new DateTime(Convert.ToInt32(year.Key), 3, 30),
new DateTime(Convert.ToInt32(year.Key), 3, 31),
new DateTime(Convert.ToInt32(year.Key), 4, 1),
new DateTime(Convert.ToInt32(year.Key), 4, 2),
new DateTime(Convert.ToInt32(year.Key), 4, 25),
new DateTime(Convert.ToInt32(year.Key), 5, 7),
new DateTime(Convert.ToInt32(year.Key), 8, 15),
new DateTime(Convert.ToInt32(year.Key), 9, 1),
new DateTime(Convert.ToInt32(year.Key), 12, 25),
new DateTime(Convert.ToInt32(year.Key), 12, 26)
};
foreach (DateTime day in days)
{
if (holidayState.Contains(day))
{
daysToAdd.Add(day);
}
}
}
holiday = daysToAdd;
return holiday == null ? 0 : holidayState.Count(date => date.DayOfWeek == dayOfWeek);
}
UPDATE: Using my first implementation stated above, I was able to get all dates for a given range (say 6 months). I don't know if this is what you need.
This question already has answers here:
Determine Whether Two Date Ranges Overlap
(39 answers)
Closed 4 years ago.
I am writing a scheduling program using an algorithm. At the last stage of the algorithm I need to look into the timetable (the timetable which was created) to see if the student is already assigned to a class at that time.
Therefore, we have:
Current Class Start Time: (2017, 02, 09, 10, 00, 00)
Current Class Finish Time: (2017, 02, 09, 11, 00, 00)
At this moment we will search into the timetable to see what other classes student A is assigned to:
For example, let's say on the same date they are already assigned to:
Class 'Z' Start Time: (2017, 02, 09, 09, 00, 00)
Class 'Z' Finish Time: (2017, 02, 09, 12, 00, 00)
Now I want to find the time range of Class 'Z' and compare it with the time range of Current Class.
DateTime startClassZ = new DateTime(2017, 02, 09, 09, 00, 00);
DateTime endClassZ = new DateTime(2017, 02, 09, 12, 00, 00);
DateTime StartCurrent = new DateTime(2017, 02, 09, 10, 00, 00);
DateTime StartCurrent = new DateTime(2017, 02, 09, 11, 00, 00);
if (They do not clash)
{
Assign
}
if (Clash)
{
Select Another Student
}
Could anyone please help me with my 'IF statements' and also how to solve this problem.
The way that I am thinking of it, there are three possibilities:
If 'current class' (Start and end time) lands in between 'Class Z' (Clash 1)
If the 'start time' of 'current class' lands in between 'Class Z' (Clash 2)
If the 'end time' of 'current class' lands in between 'Class Z' (Clash 3)
Thank You
This is the simplest way to do it:
public static bool HasOverlap(DateTime start1, DateTime end1, DateTime start2, DateTime end2)
{
return start1 < end2 && end1 > start2;
}
Or if the dates aren't necessarily in the correct start/end order:
public static bool HasOverlap(DateTime start1, DateTime end1, DateTime start2, DateTime end2)
{
return Min(start1, end1) < Max(start2, end2) && Max(start1, end1) > Min(start2, end2);
}
public static DateTime Max(DateTime d1, DateTime d2)
{
return d1 > d2 ? d1 : d2;
}
public static DateTime Min(DateTime d1, DateTime d2)
{
return d2 > d1 ? d1: d2;
}
Note, if a class ends at 2 and the next starts at 2 there will be no overlap. Since you're talking about classes, I'm assuming this is how you want it.
Testing your example:
static void Main(string[] args)
{
DateTime startClassZ = new DateTime(2017, 02, 09, 09, 00, 00);
DateTime endClassZ = new DateTime(2017, 02, 09, 12, 00, 00);
DateTime StartCurrent = new DateTime(2017, 02, 09, 10, 00, 00);
DateTime EndCurrent = new DateTime(2017, 02, 09, 11, 00, 00);
if(HasOverlap(startClassZ, endClassZ, StartCurrent, EndCurrent))
{
Console.WriteLine("clash");
}
else
{
Console.WriteLine("yay");
}
Console.Read();
}
I've added some quick tests for you:
public static void Test1()
{
// Class A overlaps class B
DateTime aStart = DateTime.Parse("2017-01-01T09:00:00");
DateTime aEnd = DateTime.Parse("2017-01-01T10:00:00");
DateTime bStart = DateTime.Parse("2017-01-01T09:30:00");
DateTime bEnd = DateTime.Parse("2017-01-01T11:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == true;
Console.WriteLine($"1: {isCorrect}");
}
public static void Test2()
{
// Class A "surrounds" class B
DateTime aStart = DateTime.Parse("2017-01-01T09:00:00");
DateTime aEnd = DateTime.Parse("2017-01-01T15:00:00");
DateTime bStart = DateTime.Parse("2017-01-01T09:30:00");
DateTime bEnd = DateTime.Parse("2017-01-01T11:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == true;
Console.WriteLine($"2: {isCorrect}");
}
public static void Test3()
{
// Class B "surrounds" class A
DateTime aStart = DateTime.Parse("2017-01-01T09:30:00");
DateTime aEnd = DateTime.Parse("2017-01-01T11:00:00");
DateTime bStart = DateTime.Parse("2017-01-01T09:00:00");
DateTime bEnd = DateTime.Parse("2017-01-01T15:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == true;
Console.WriteLine($"3: {isCorrect}");
}
public static void Test4()
{
// Class A is before Class B
DateTime aStart = DateTime.Parse("2017-01-01T09:00:00");
DateTime aEnd = DateTime.Parse("2017-01-01T11:00:00");
DateTime bStart = DateTime.Parse("2017-01-01T11:00:00");
DateTime bEnd = DateTime.Parse("2017-01-01T12:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == false;
Console.WriteLine($"4: {isCorrect}");
}
public static void Test5()
{
// Class A is after Class B
DateTime aStart = DateTime.Parse("2017-01-01T12:00:00");
DateTime aEnd = DateTime.Parse("2017-01-01T14:00:00");
DateTime bStart = DateTime.Parse("2017-01-01T11:00:00");
DateTime bEnd = DateTime.Parse("2017-01-01T12:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == false;
Console.WriteLine($"5: {isCorrect}");
}
public static void Test6()
{
// Class B overlaps class A
DateTime bStart = DateTime.Parse("2017-01-01T09:00:00");
DateTime bEnd = DateTime.Parse("2017-01-01T10:00:00");
DateTime aStart = DateTime.Parse("2017-01-01T09:30:00");
DateTime aEnd = DateTime.Parse("2017-01-01T11:00:00");
bool isCorrect = HasOverlap(aStart, aEnd, bStart, bEnd) == true;
Console.WriteLine($"6: {isCorrect}");
}
static void Main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Console.Read();
}
You are offering three possibilities:
A) Whole currentClass is in zClass
B) Start of currentClass is in zClass
C) End of currentClass is in zClass
I would like to point out that A) means the same as B) and C) combined, thus does not need a special attention. Moreover, if you will try and draw a simplest sketch in your mind (or on paper) of duration of classes, then the conditions will pop themselves out quite naturally as:
clash = (endOfCurrentClass > startOfZClass) || (startOfCurrentClass < endOfZClass)
What remains is to think about equivalence (e.g. endOfCurrentClass == startOfZClass) but this depends on other constraints (does class-times incorporate pauses in between classes? Are they in a same room? ...). However this is another problem.
You need double check:
classZ with current in this case
DateTime startClassZ = new DateTime(2017, 02, 09, 09, 00, 00);
DateTime endClassZ = new DateTime(2017, 02, 09, 12, 00, 00);
DateTime startCurrent = new DateTime(2017, 02, 10, 10, 00, 00);
DateTime endCurrent = new DateTime(2017, 02, 09, 11, 00, 00);
and current with classZ
DateTime startClassZ = new DateTime(2017, 02, 09, 09, 00, 00);
DateTime endClassZ = new DateTime(2017, 02, 09, 12, 00, 00);
DateTime startCurrent = new DateTime(2017, 02, 08, 10, 00, 00);
DateTime endCurrent = new DateTime(2017, 02, 09, 13, 00, 00);
Full code is here:
DateTime startClassZ = new DateTime(2017, 02, 09, 09, 00, 00);
DateTime endClassZ = new DateTime(2017, 02, 09, 12, 00, 00);
DateTime startCurrent = new DateTime(2017, 02, 08, 10, 00, 00);
DateTime endCurrent = new DateTime(2017, 02, 09, 13, 00, 00);
bool ClashCurrentStart = startClassZ < startCurrent && startCurrent < endClassZ;
bool ClashCurrentEnd = startClassZ < endCurrent && endCurrent < endClassZ;
//bool ClashCurrentBetween = ClashCurrentStart && ClashCurrentEnd;
bool ClashClassZStart = startCurrent < startClassZ && startClassZ < endCurrent;
bool ClashClassZEnd = startCurrent < endClassZ && endClassZ < endCurrent;
//bool ClashClassZBetween = ClashClassZStart && ClashClassZEnd;
bool Clash = ClashCurrentStart || ClashCurrentEnd || ClashClassZStart || ClashClassZEnd;
if (!Clash)
{
//Assign
}
else // Clash
{
//Select Another Student
}
I have a c# list containing periods using cDates class.
public class cDates
{
public Date PeriodStart {get;set;}
public Date PeriodEnd {get;set;}
}
2014/01/01 - 2014/04/30
2014/05/01 - 2014/07/31
2014/08/01 - 2014/09/30
Is it possible to get a list of dates containing all dates in existing periods, something like List.
public class cSingleDate
{
public Date SingleDate {get;set;}
}
2014/01/01
2014/01/02
2014/01/03
...
2014/09/30
I tried to find a solution using loops, but want to find out is there a cleaner solution. Maybe a Linq solution?
You can use LINQ for that:
[TestMethod]
public void TestPeriods()
{
var periods = new[]
{
new Period(new DateTime(2014, 01, 01), new DateTime(2014, 04, 30)),
new Period(new DateTime(2014, 05, 01), new DateTime(2014, 07, 31)),
new Period(new DateTime(2014, 08, 01), new DateTime(2014, 09, 30)),
};
var days = from period in periods
let numberOfDays = (period.LastDay - period.FirstDay).Days + 1
from day in Enumerable.Range(0, numberOfDays)
select period.FirstDay.AddDays(day);
var distinctDays = days.Distinct().ToArray();
distinctDays.Should().Contain(new DateTime(2014, 01, 01));
distinctDays.Should().Contain(new DateTime(2014, 02, 01));
distinctDays.Should().Contain(new DateTime(2014, 04, 30));
distinctDays.Should().NotContain(new DateTime(2014, 10, 01));
}
public class Period
{
public Period(DateTime firstDay, DateTime lastDay)
{
this.FirstDay = firstDay;
this.LastDay = lastDay;
}
public DateTime FirstDay {get;set;}
public DateTime LastDay {get;set;}
}
You can just loop through dates, and add one day to PeriodStart as long as it's less than or equal to PeriodEnd:
var dates = new List<cSingleDate>();
foreach(var date in cDates)
{
while(date.PeriodStart <= date.PeriodEnd)
{
dates.Add(new cSingleDate { SingleDate = date.PeriodStart });
date.PeriodStart = date.PeriodStart.AddDays(1);
}
}
If you don't wanna change cDates just use a temporary variable to store PeriodStart.
There are two easy ways to transform the dates from the Period class into a single list of dates. You can either extend the Period class to give you the dates and use linq to flatten it or you read both properties with linq. If the SingleDate class really only stores the date and nothing else and if it does not provide any additional functionality you may consider to just extract the dates.
// Test data.
var periods = new[]
{
new Period(new DateTime(2014, 01, 01), new DateTime(2014, 04, 30)),
new Period(new DateTime(2014, 05, 01), new DateTime(2014, 07, 31)),
new Period(new DateTime(2014, 08, 01), new DateTime(2014, 09, 30)),
};
If you can make the Period class to implement IEnumerable<DateTime> interface and thus flatten the dates for you then a simple SelectMany can be used:
var result1 =
periods
// The Period class will enumerate the dates for SelectMany:
.SelectMany(x => x)
// Uncomment this line if you want to encapsulate the date.
//.Select(x => new SingleDate() { Date = x })
.ToList();
Otherwise you need to flatten the Period dates inside the SelectMany:
var resul2 =
periods
// Here SelectMany has to enumerate the dates:
.SelectMany(x => new[]
{
x.PeriodStart,
x.PeriodEnd
})
// Uncomment this line if you want to encapsulate the date.
//.Select(x => new SingleDate() { Date = x })
.ToList();
Period-class that implements the IEnumerable<DateTime> interface to enumerate the dates:
public class Period : IEnumerable<DateTime>
{
public Period(DateTime firstDay, DateTime lastDay)
{
this.PeriodStart = firstDay;
this.PeriodEnd = lastDay;
}
public DateTime PeriodStart { get; set; }
public DateTime PeriodEnd { get; set; }
public IEnumerator<DateTime> GetEnumerator()
{
yield return PeriodStart;
yield return PeriodEnd;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
SingleDate-class without changes (you don't need it if it does not provide any additional functionality):
public class SingleDate
{
public DateTime Date { get; set; }
}
So I'm building a meeting scheduler, in which each potential participant has a set of unavailable dates. For the sake of testing I inserted my own.
The user enters a date range, which is then put into a list of all the dates in-between.
I currently have a list of unavailable dates depending on who the user has invited to the event. This is saved into a list called unavailableDates.
I'm trying to delete all the dates that exist in the datesBetween list that are also in the unavailableDates list, to leave only dates that aren't on a persons unavailable dates.
I've tried a few different things but i cant seem to get it working.
private void compileDates()
{
DateTime startingDate = cal1.SelectionEnd; //Get the starting date from the first calender
DateTime endingDate = cal2.SelectionEnd; //Get the ending date from the first calender
var namesList = lstConfirmedParticipants.Items.Cast<String>().ToList(); //Collects all the participants names and converts it to a list
List<DateTime> PhillChambers = new List<DateTime>(); //Set Pauls Unavailable Dates
PhillChambers.Add(new DateTime(2014, 08, 11));
PhillChambers.Add(new DateTime(2014, 08, 12));
PhillChambers.Add(new DateTime(2014, 08, 17));
List<DateTime> HenryWright = new List<DateTime>(); //Set Pauls Unavailable Dates
HenryWright.Add(new DateTime(2014, 08, 09));
HenryWright.Add(new DateTime(2014, 08, 12));
HenryWright.Add(new DateTime(2014, 08, 14));
List<DateTime> PaulaCooper = new List<DateTime>(); //Set Pauls Unavailable Dates
PaulaCooper.Add(new DateTime(2014, 08, 11));
PaulaCooper.Add(new DateTime(2014, 08, 12));
PaulaCooper.Add(new DateTime(2014, 08, 16));
List<DateTime> unavailableDates = new List<DateTime>(); //Creates a new list to hold all the unavailable dates
if (namesList.Contains("Paula Cooper"))
{ //Add Paulas Unavailable Dates
unavailableDates.AddRange(PaulaCooper);
}
if (namesList.Contains("Henry Wright"))
{ //Add Henrys Unavailable Dates
unavailableDates.AddRange(HenryWright);
}
if (namesList.Contains("Phill Chambers"))
{ //Add Phills Unavailable Dates
unavailableDates.AddRange(PhillChambers);
}
foreach (DateTime date in GetDateRange(startingDate, endingDate))
{
lstDatesBetween.Items.Add(date.ToShortDateString()); //Get all the dates between the date ranges and put them into the listbox.
}
List<DateTime> datesBetween = lstDatesBetween.Items.OfType<DateTime>().ToList(); //Convert the Listbox into a list holding all the dates between the date ranges
datesBetween.RemoveAll(item => unavailableDates.Contains(item)); //remove all the dates in dates between that also appear in unavailable dates
List<DateTime> availableDates = new List<DateTime>(); //Creates a new list to hold all the available dates(FUTURE USE)
availableDates.AddRange (datesBetween);
lstDatesAvailable.DataSource = availableDates; //display the available dates for the meeting
private List<DateTime> GetDateRange(DateTime StartingDate, DateTime EndingDate)
{
if (StartingDate > EndingDate)
{
return null;
}
List<DateTime> datesBetween = new List<DateTime>();
DateTime tempDate = StartingDate;
do
{
datesBetween.Add(tempDate);
tempDate = tempDate.AddDays(1);
} while (tempDate <= EndingDate);
return datesBetween;
}
It seems that lstDatesBetween is populated with strings:
lstDatesBetween.Items.Add(date.ToShortDateString());
and then datesBetween takes elements of type DateTime:
List<DateTime> datesBetween = lstDatesBetween.Items.OfType<DateTime>().ToList();
So I guess datesBetween is empty.
Try if this works:
List<DateTime> datesBetween = lstDatesBetween.Items
.OfType<string>()
.Select(s => Convert.ToDateTime(s))
.ToList();