I’m looking for a way to find holes in a schedule, times that does not have a booking.
I have a simple class in C# that looks like:
DateTime StartTime { get; set; }
Datetime EndTime { get; set; }
public int User_ID { get; set; }
The same class is used for the bookings aswell.
Let’s assume I have these objects:
Schedule: StartTime "2017-03-14 08:00" - EndTime "2017-03-14 16:00" (8 hours)
Booking: StartTime "2017-03-14 09:00" - Endtime "2017-03-14 10:00" (1 hour)
My final result from this would be 2 objects that represents the “free time”:
Free: StartTime "2017-03-14 08:00" EndTime: "2017-03-14 09:00" (1 hour)
Free: StartTime "2017-03-14 10:00" EndTime: "2017-03-14 16:00"(6 hour)
How would I check this in C#?
I'm thinking about looping the Schedule and split them on start/end of each booking, but I'm not sure how to do it.
It is easier than I thought... Note that this code isn't optimized, and this algorithm isn't probably very optimizable:
public class TimeSegment
{
public readonly DateTime StartTime;
public readonly DateTime EndTime;
public TimeSegment(DateTime startTime, DateTime endTime)
{
StartTime = startTime;
EndTime = endTime;
}
public TimeSegment[] Subtract(TimeSegment other)
{
// 8-10 Subtract 10-11 = 8-10
if (StartTime > other.EndTime || other.StartTime > EndTime)
{
// If there is no intersection, we return { this }
// (no subtraction)
return new[] { this };
}
if (StartTime >= other.StartTime)
{
// 8-10 Subtract 8-10 = (nothing)
// 8-10 Subtract 7-11 = (nothing)
if (EndTime <= other.EndTime)
{
// Total subtraction, nothing remains!
return new TimeSegment[0];
}
else
{
// 8-10 Subtract 7-9 = 9-10
return new[] { new TimeSegment(other.EndTime, EndTime) };
}
}
// 8-12 Subtract 9-13 = 8-9
if (EndTime <= other.EndTime)
{
return new[] { new TimeSegment(StartTime, other.EndTime) };
}
// 8-12 Subtract 9-11 = 8-9, 11-12
// Complete case: two TimeSegments returned
return new[] { new TimeSegment(StartTime, other.StartTime), new TimeSegment(other.EndTime, EndTime) };
}
public override string ToString()
{
return string.Format("{0}-{1}", StartTime, EndTime);
}
}
And then:
var schedules = new List<TimeSegment> { new TimeSegment(new DateTime(2017, 03, 14, 08, 00, 00), new DateTime(2017, 03, 14, 16, 00, 00)) };
var bookings = new List<TimeSegment>
{
new TimeSegment(new DateTime(2017, 03, 14, 09, 00, 00), new DateTime(2017, 03, 14, 10, 00, 00)),
new TimeSegment(new DateTime(2017, 03, 14, 12, 00, 00), new DateTime(2017, 03, 14, 14, 00, 00)),
new TimeSegment(new DateTime(2017, 03, 14, 13, 00, 00), new DateTime(2017, 03, 14, 15, 00, 00)),
};
foreach (TimeSegment booking in bookings)
{
var schedulesNew = new List<TimeSegment>();
foreach (TimeSegment schedule in schedules)
{
var diff = schedule.Subtract(booking);
schedulesNew.AddRange(diff);
}
schedules = schedulesNew;
}
There "core" of this is a Subtract function that given a TimeSegment subtracts from this another TimeSegment, returning 0, 1 or 2 TimeSegments... Then iteratively we subtract all the bookings from the TimeSegments that we produced from the previous booking.
Related
I have a small problem because I do not always understand how to use the lessons of the day, for example, I want the time from the list to have the day time recalculated from that date, but if I have a new time, that conversion counts from the new time. It works fine for me if I only have one time, but if I have two times, foreach the loop calculates me both times for the day.
This is my code:
public TimeSpan GetHoursForDay(DateTime day) {
TimeSpan time = TimeSpan.Zero;
foreach (var times in shouldWorkTime)
{
if (times.Valid_from > day) //here's the real problem for me, do i want the hours to count from that date, for example: for 1.1.2020 it doesn't need to take hours from 1.12.2019
continue;
if (day.DayOfWeek == DayOfWeek.Monday)
{
time += times.ShouldWorkMonday;
}
if (day.DayOfWeek == DayOfWeek.Tuesday)
{
time += times.ShouldWorkTuesday;
}
if (day.DayOfWeek == DayOfWeek.Wednesday)
{
time += times.ShouldWorkWednesday;
}
if (day.DayOfWeek == DayOfWeek.Thursday)
{
time += times.ShouldWorkThursday;
}
if (day.DayOfWeek == DayOfWeek.Friday)
{
time += times.ShouldWorkFriday;
}
if (day.DayOfWeek == DayOfWeek.Saturday)
{
time += times.ShouldWorkSaturday;
}
if (day.DayOfWeek == DayOfWeek.Sunday)
{
time += times.ShouldWorkSunday;
}
}
return time;
}
}
These are the values I get in the list:
var shouldWorkTime = new List<ShouldWorkTime>
{
new ShouldWorkTime
{
Valid_from = new DateTime(2019, 12, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(8,0,0),
ShouldWorkTuesday= new TimeSpan(7,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(8,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
},
new ShouldWorkTime
{
Valid_from = new DateTime(2020, 01, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(4,0,0),
ShouldWorkTuesday= new TimeSpan(3,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(9,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
}
};
for the day value, I always get for the current day from the calendar, so I want to be counted in this case for the days of 1.1.2020 values for the days that fall in the second count, and until then the values that fall in the first count.
so i need to return how many hours for a particular day a worker needs to make, but valid from the last date from (Valid_From).
How can I correct this? thank you all very much for your help
eg:
input 1.1.2020 output = 6,0,0;
input 1.12.2019 output = 0,0,0;
Here is a little modification of the Data structure.
Instead of a brunch of property The TimeSpan will be mapped to the DayOfWeek using a Dictionary. This will remove the need for a switch-case or a lot of If.
Using DayOfWeek as dictionary key ensure that only one TimeSpan is defined for a day.
public class WorkingTimeScheldure
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public Dictionary<DayOfWeek, TimeSpan> Scheldure { get; set; }
}
That way I can ask for the TimeSpan of a day using : Scheldure[test.DayOfWeek]
Note the addition of an End property. It may be usefull to select the right thing.
eg: 30/01/2020 is superior to 01/12/2019 and 01/01/2020..
So in order to take the most recent, I assume the list in ordered on Start date and pick the last one: .Last(x => x.Start <= day).
Online Demo
public class Program
{
static List<ShouldWork> WorkTimeScheldure;
public static void Main()
{
WorkTimeScheldure = new List<ShouldWork>
{
new ShouldWork
{
Start = new DateTime(2019, 12, 01, 0, 0, 0),
Scheldure= new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(8,0,0)},
{(DayOfWeek)2, new TimeSpan(7,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(8,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
},
new ShouldWork
{
Start = new DateTime(2020, 01, 01, 0, 0, 0),
Scheldure = new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(4,0,0)},
{(DayOfWeek)2, new TimeSpan(3,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(9,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
}
};
var testValues = new[] {
new DateTime(2019, 12, 01, 0, 0, 0),
new DateTime(2019, 12, 02, 0, 0, 0),
new DateTime(2019, 12, 03, 0, 0, 0),
new DateTime(2019, 12, 04, 0, 0, 0),
new DateTime(2019, 12, 05, 0, 0, 0),
new DateTime(2019, 12, 06, 0, 0, 0),
new DateTime(2019, 12, 07, 0, 0, 0),
new DateTime(2019, 12, 08, 0, 0, 0),
new DateTime(2020, 01, 01, 0, 0, 0),
new DateTime(2020, 01, 02, 0, 0, 0),
new DateTime(2020, 01, 03, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 06, 0, 0, 0),
new DateTime(2020, 01, 07, 0, 0, 0),
new DateTime(2020, 01, 08, 0, 0, 0),
};
foreach (var test in testValues) {
// Perhaps there is many possible, so I took the Last.
var workingTime = WorkTimeScheldure.Last(x => x.Start <= day);
//Please handle the case where there is no matching scheludre for this date.
var houtToWork = workingTime.Scheldure[day.DayOfWeek].Hours;
Console.WriteLine(
$"{day.ToShortDateString()} , it's a {day.DayOfWeek}" +
$" I have to work {houtToWork} Hour{(houtToWork>1?"s":"")}!"
);
}
}
}
Result :
12/01/2019 , it's a Sunday I have to work 0 Hour!
12/02/2019 , it's a Monday I have to work 8 Hours!
12/03/2019 , it's a Tuesday I have to work 7 Hours!
12/04/2019 , it's a Wednesday I have to work 6 Hours!
12/05/2019 , it's a Thursday I have to work 5 Hours!
12/06/2019 , it's a Friday I have to work 8 Hours!
12/07/2019 , it's a Saturday I have to work 0 Hour!
12/08/2019 , it's a Sunday I have to work 0 Hour!
01/01/2020 , it's a Wednesday I have to work 6 Hours!
01/02/2020 , it's a Thursday I have to work 5 Hours!
01/03/2020 , it's a Friday I have to work 9 Hours!
01/04/2020 , it's a Saturday I have to work 0 Hour!
01/05/2020 , it's a Sunday I have to work 0 Hour!
01/06/2020 , it's a Monday I have to work 4 Hours!
01/07/2020 , it's a Tuesday I have to work 3 Hours!
01/08/2020 , it's a Wednesday I have to work 6 Hours!
The test times.Valid_from > day is false for all element in shouldWorkTime if the value of day is big enough. That why time can be incremented multiple time.
If you want to increment only once and on the first/last acceptable value of Valid_from, you should ensure that shouldWorkTime is sorted in increasing/decreasing order and ensure that the increment is done only once.
In fact you didn't need increment but just return the corresponding TimeSpan:
public TimeSpan GetHoursForDay(DateTime day) {
// shouldWorkTime should have been sorted once for all at creation.
// This code use the first acceptable Valid_from
// By using OrderByDescending we take the last (in date) entry
var math = shouldWorkTime
.Where(v => day >= v.Valid_from) // We take only valid entry
.OrderByDescending(v => v.Valid_from) // We sort only on valid entry
.FirstOrDefault(); // we take the last (in date) valid entry
if (match == null)
return TimeSpan.Zero;
switch (day.DayOfWeek)
{
case DayOfWeek.Monday:
return match.ShouldWorkMonday;
case DayOfWeek.Tuesday:
return match.ShouldWorkTuesday;
case DayOfWeek.Wednesday:
return match.ShouldWorkWednesday;
case DayOfWeek.Thursday:
return match.ShouldWorkThursday;
case DayOfWeek.Friday:
return match.ShouldWorkFriday;
case DayOfWeek.Saturday:
return match.ShouldWorkSaturday;
case DayOfWeek.Sunday:
return match.ShouldWorkSunday;
}
}
Edit:
To avoid code duplication, the ShouldWorkTime class may provide a GetWorkTimeForDayOfWeek:
public TimeSpan GetWorkTimeForDayOfWeek(DayOfWeek dayOfWeek) {
...
}
And instead of storing seven values in seven fields, you may take a look to collections. I will choose a Dictionary<DayOfWeek, TimeSpan>.
I have a Journey object that has a DateTime JourneyStartTime { get; set; } and a DateTime JourneyEndTime { get; set; }. I want to calculate the total number of minutes that the journey spent between 10pm and 6am (note this goes over midnight).
I have tried using TimeSpans for indicating the 10pm and 6am, but I am not sure if that is the best data type to use.
The domain for this logic is insurance based. Company X wants to score drivers that drive between X - Y hours. Those hours ought to be configurable. Here's a scenario:
A journey takes place on the same day between 5pm and 6pm. Company X Inurance is interested in journeys between 10pm and 6am. How many minutes did that journey spend in the time period that Company X is interested in?
The answer to the above is: 0, but my code is giving 60 minutes (here is a dotnetFiddle).
Here is the code.
CODE
using System;
public class Program
{
public static void Main()
{
var shortSameDayJourney = new Journey {
JourneyId = 1,
// start of journey - 5pm - start
JourneyStartTime = new DateTime(2018, 12, 17, 17, 00, 00, DateTimeKind.Utc),
// end of journey - 6pm - end
JourneyEndTime = new DateTime(2018, 12, 17, 18, 00, 00, DateTimeKind.Utc)
};
var scoreTimePeriod = new InsurerTimePeriodScoreSetting {
// start of insurer's time period.
StartOfTimePeriod = DateTime.Now + TimeSpan.FromHours(22),
// end of insurer's time period.
EndOfTimePeriod = DateTime.Now + TimeSpan.FromHours(30)
};
var minutesInTimePeriod = getNumberOfMinutesThatJourneyWasInTimePeriod(shortSameDayJourney, scoreTimePeriod);
Console.WriteLine("Number of minutes the journey was within the time period the insurer had sepcified:");
Console.WriteLine(minutesInTimePeriod + " minutes");
}
public static double getNumberOfMinutesThatJourneyWasInTimePeriod(
Journey journey,
InsurerTimePeriodScoreSetting insurerTimePeriod) {
var JourneyStart = journey.JourneyStartTime;
var JourneyEnd = journey.JourneyEndTime;
var timeSpan = insurerTimePeriod.EndOfTimePeriod - insurerTimePeriod.StartOfTimePeriod;
var startDif = (JourneyStart - insurerTimePeriod.StartOfTimePeriod);
var endDif = (insurerTimePeriod.EndOfTimePeriod - JourneyEnd);
var time = timeSpan - startDif - endDif;
return time.TotalMinutes;
}
}
public class Journey {
public int JourneyId {get;set;}
// journey start date and time in UTC, comes form a tracking device on vehicle.
public DateTime JourneyStartTime {get;set;}
// journey end date and time in UTC, comes form a tracking device on vehicle.
public DateTime JourneyEndTime {get;set;}
}
public class InsurerTimePeriodScoreSetting {
public DateTime StartOfTimePeriod {get;set;}
public DateTime EndOfTimePeriod {get;set;}
}
Timespan only gives your raw time between 2 DateTime's
so i had to changed your Journey initialization so i will be able to compare on the same day
var shortSameDayJourney = new Journey
{
JourneyId = 1,
// start of journey - 5pm - start
JourneyStartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 17, 00, 00, DateTimeKind.Utc),
// end of journey - 6pm - end
JourneyEndTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 18, 00, 00, DateTimeKind.Utc)
};
same for InsurerTimePeriodScoreSetting
var scoreTimePeriod = new InsurerTimePeriodScoreSetting
{
// start of insurer's time period. 18/12 22:00
StartOfTimePeriod = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 22, 0, 0, DateTimeKind.Utc), // DateTime.Now + TimeSpan.FromHours(22),
// end of insurer's time period. 19/12 6:00
EndOfTimePeriod = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 1, 6, 0, 0, DateTimeKind.Utc) // DateTime.Now + TimeSpan.FromHours(30)
};
now all you need to do is a simple check - if Journey time is between InsurerTimePeriodScoreSetting
if (JourneyStart >= insurerTimePeriod.StartOfTimePeriod && JourneyEnd <= insurerTimePeriod.EndOfTimePeriod)
{
// your same calculation here
}
else
return 0;
I have already asked a different question regarding Sorting Date Time and got help from another user to pass my values. I am using a for loop like below, but definitely am wrong here because the code brings the value one by one rather than sorting.
public class Break
{
public DateTime MealStart { get; set; }
public DateTime MealEnd { get; set; }
}
my main class
IList<DateTime> starts = new List<DateTime>();
IList<DateTime> ends = new List<DateTime>();
DateTime breakStart1 = new DateTime(2012, 02, 15, 12, 30, 00); // 15/02/12 12.30PM
DateTime breakEnd1 = new DateTime(2012, 02, 15, 13, 30, 00); // 15/02/12 01.30PM
DateTime breakStart2 = new DateTime(2012, 02, 15, 11, 00, 00); // 15/02/12 11.00AM
DateTime breakEnd2 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakStart3 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakEnd3 = new DateTime(2012, 02, 15, 01, 00, 00); // 15/02/12 01.00PM
starts.Add(breakStart1);
starts.Add(breakStart2);
starts.Add(breakStart3);
ends.Add(breakEnd1);
ends.Add(breakEnd2);
ends.Add(breakEnd3);
for (int i = 0; i < starts.Count; i++)
{
var breaks = new List<Break>()
{
//for (int j= 0; j<starts.Count; j++)
//{
new Break()
{
MealStart = starts[i],
MealEnd = ends[i]
}
// }
};
var ordered = breaks.OrderBy(s => s.MealStart);
foreach (var ord in ordered)
{
System.Console.WriteLine(ord.MealStart);
System.Console.WriteLine(ord.MealEnd);
}
}
I am expecting a result like below
breakStart1 = 15/02/12 11.00AM
breakEnd1= 15/02/12 12.00PM
breakStart2 = 15/02/12 12.00PM
breakEnd2= 15/02/12 01.00PM
breakStart3 = 15/02/12 12.30PM
breakEnd3= 15/02/12 01.30PM
but it's not because of the for loop.
You are creating breaks after ever loop, you need to do this outside of the loop like this:
IList<DateTime> starts = new List<DateTime>();
IList<DateTime> ends = new List<DateTime>();
DateTime breakStart1 = new DateTime(2012, 02, 15, 12, 30, 00); // 15/02/12 12.30PM
DateTime breakEnd1 = new DateTime(2012, 02, 15, 13, 30, 00); // 15/02/12 01.30PM
DateTime breakStart2 = new DateTime(2012, 02, 15, 11, 00, 00); // 15/02/12 11.00AM
DateTime breakEnd2 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakStart3 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakEnd3 = new DateTime(2012, 02, 15, 01, 00, 00); // 15/02/12 01.00PM
starts.Add(breakStart1);
starts.Add(breakStart2);
starts.Add(breakStart3);
ends.Add(breakEnd1);
ends.Add(breakEnd2);
ends.Add(breakEnd3);
List<Break> breaks = new List<Break>();
for (int i = 0; i < starts.Count; i++)
{
breaks.Add(new Break()
{
MealStart = starts[i],
MealEnd = ends[i]
});
}
var ordered = breaks.OrderBy(s => s.MealStart);
foreach (var ord in ordered)
{
System.Console.WriteLine(ord.MealStart);
System.Console.WriteLine(ord.MealEnd);
}
Since #Corylulu beat me to the punch on the basic issue, here's a different method that is slightly shorter:
IEnumerable<Break> breaks =
starts.Zip(ends, (s, e) => new Break { MealStart = s, MealEnd = e })
.OrderBy(b => b.MealStart);
foreach (Break brk in breaks)
Console.WriteLine("Start: {0}\tEnd: {1}", brk.BreakStart, brk.BreakEnd);
The IEnumerable.Zip method takes a pair of IEnumerables and a transform function and produces an output IEnumerable containing the results of calling the transform function with members of each input IEnumerable. You could convert it to a List<Break> with a ToList() at the end of course.
I want to get year from date in mongodb.
This is my simple script :
DateTime sDate = DateTime.SpecifyKind(new DateTime(2013, 1, 15, 00, 00, 00), DateTimeKind.Utc);
DateTime eDate = DateTime.SpecifyKind(new DateTime(2014, 12, 31, 23, 59, 59), DateTimeKind.Utc);
string map = # "function(){
emit (_id.OwnerId, _id.Date {
Date: Value.Date.getYear(),
PurchaseAmount: parseFloat(Value.PurchaseAmount),
PurchaseReturnAmount: parseFloat(Value.PurchaseReturnAmount),
TotalAmount: parseFloat(Value.TotalAmount)
});
}";
string reduce = # "function (key, values) {
var outValue = {purchsaeAmount:0 , purchaseReturnAmount:0, totalAmount:0 }
values.forEach(function (value) {
outValue.PurchaseAmount +=parseFloat(value.PurchaseAmount);
outValue.PurchaseReturnAmount +=parseFloat(value.PurchaseReturnAmount);
outValue.TotalAmount +=parseFloat(value.TotalAmount);
});
return outValue;
}";
In map I want to get only year, how can I get start year(2013) and end year(2014)?
var d = new Date();
var n = d.getFullYear();
var next_year = d.getFullYear()+1
out:
2013 2014
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() );