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 would like to know the best way to set a range of dates. I have logic that checks what day a certain record is approved on and based on that day i set a date the next time that record needs to be re-approved
so if the record is approved in january or february it should be re evaluated in march if its approved in march it should be re evaluated in june.
i declare my ranges like this for now, but this i believe is not the best way to do it
DateTime quarterOneStart = new DateTime(DateTime.Now.Year,07,01);
DateTime quarterOneEnd = new DateTime(DateTime.Now.Year, 09, 15));
DateTime quarterTwoStart = new DateTime(DateTime.Now.Year, 10, 01);
DateTime quarterTwoEnd = new DateTime(DateTime.Now.Year, 12, 15));
DateTime quarterThreeStart = new DateTime(DateTime.Now.Year, 01, 01);
DateTime quarterThreeEnd = new DateTime(DateTime.Now.Year, 03, 15));
DateTime quarterFourStart = new DateTime(DateTime.Now.Year, 04, 01);
DateTime quarterFourEnd = new DateTime(DateTime.Now.Year, 06, 15));
is there a better way to set the date time variables above?
What about class for quarter
public class Quarter {
private readonly DateTime _startDate;
private readonly DateTime _endDate;
public Quarter(DateTime startDate, DateTime endDate) {
_startDate = startDate;
_endDate = endDate;
}
public DateTime StartDate => _startDate;
public DateTime EndDate => _endDate;
}
and use it
Quarter one = new Quarter(new DateTime(2017, 07, 01), new DateTime(2017, 09, 15));
Quarter two = new Quarter(new DateTime(2017, 09, 15), new DateTime(2017, 10, 01));
...
Rather than creating multiple variables, you can create a Dictionary of items key'ed by an enum. For example:
public enum Quaters
{
Q1_Start,
Q1_End,
Q2_Start,
Q2_End,
Q3_Start,
Q3_End,
Q4_Start,
Q4_End
}
Dictionary<Quaters, DateTime> dateRange = new Dictionary<Quaters, DateTime>
{
{Quaters.Q1_Start, new DateTime(DateTime.Now.Year, 07, 01)},
{Quaters.Q1_End, new DateTime(DateTime.Now.Year, 09, 15)},
{Quaters.Q2_Start, new DateTime(DateTime.Now.Year, 10, 01)},
{Quaters.Q2_End, new DateTime(DateTime.Now.Year, 12, 15)},
...
};
When you need to verify the value against any range parameter you can index the dateRange dictionary based on keys. It is just a proposal and there could be better solutions to your actual problem.
In the past I've written a FinancialYear class that internally holds a startYear (int) variable and offers various methods/properties, e.g. (among others)
public DateTime StartDate
{
get { return new DateTime(_startYear, 4, 1); } // April 1st
}
public static FinancialYear ForDate(DateTime dt)
{
DateTime finYearStart = new DateTime(dt.Year, 4, 1);
return (dt >= finYearStart) ? new FinancialYear(dt.Year) : new FinancialYear(dt.Year - 1);
}
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 list of "event" objects.
In every event i have "EventStartTime" and "EventEndTime" declared as DateTime objects.
I want to be able to search "events" by time , for example 10:00,
the "event" you see below shows that the festival starts at 22:00 on Feb 17th,
and ends at 15:00 the following day. i have a couple more like these.
new EventsManager.Event() //3
{
EventType = EventsManager.EventType.Festival,
EventName = "Twistival",
EventPlace = placeList[4],
EventStartTime =new DateTime(2017,02,17,22,0,0),
EventEndTime = new DateTime(2017,02,18,15,0,0),
EventNumberOfParticipants = 8000
},
So when i search for event that occur, or still occurring at at 10:00
i should get this event.
any suggestions?
Assuming that you have a specific time of day that you want to determine if the event covers regardless of the date it covers it on then there are 4 cases you need to consider. First if the dates are more than 1 day apart they cover all times of day. If the start is before the time of day and the end is after the time of day it will cover the time. The last two cases require that the end date be on the next day from the start date, then either the start date is before the time of day, or the end date is after the time of day. Note that this also assumes that the start date is before the end date.
var events = new List<Tuple<DateTime, DateTime>>
{
// start and end after time of day but on different days
Tuple.Create(
new DateTime(2017, 02, 17, 22, 0, 0),
new DateTime(2017, 02, 18, 15, 0, 0)),
// start and end before time of day but on different days
Tuple.Create(
new DateTime(2017, 02, 17, 9, 0, 0),
new DateTime(2017, 02, 18, 7, 0, 0)),
// start before and end after same day
Tuple.Create(
new DateTime(2017, 02, 17, 9, 0, 0),
new DateTime(2017, 02, 17, 11, 0, 0)),
// covers more than 1 day
Tuple.Create(
new DateTime(2017, 02, 17, 22, 0, 0),
new DateTime(2017, 02, 18, 22, 0, 1)),
// start after and end before on different days
Tuple.Create(
new DateTime(2017, 02, 17, 22, 0, 0),
new DateTime(2017, 02, 18, 10, 0, 0)),
// start and end before on same day
Tuple.Create(
new DateTime(2017, 02, 17, 7, 0, 0),
new DateTime(2017, 02, 17, 8, 0, 0)),
// start and end after on same day
Tuple.Create(
new DateTime(2017, 02, 17, 11, 0, 0),
new DateTime(2017, 02, 17, 12, 0, 0)),
};
var timeOfDay = new TimeSpan(0, 10, 0 ,0);
foreach (var x in events)
{
if (x.Item2 - x.Item1 > TimeSpan.FromDays(1)
|| (x.Item1.TimeOfDay < timeOfDay && x.Item2.TimeOfDay > timeOfDay)
|| (x.Item1.Date < x.Item2.Date
&& (x.Item1.TimeOfDay < timeOfDay || x.Item2.TimeOfDay > timeOfDay)))
{
Console.WriteLine(x);
}
}
Will output
(2/17/2017 10:00:00 PM, 2/18/2017 3:00:00 PM)
(2/17/2017 9:00:00 AM, 2/18/2017 7:00:00 AM)
(2/17/2017 9:00:00 AM, 2/17/2017 11:00:00 AM)
(2/17/2017 10:00:00 PM, 2/18/2017 10:00:01 PM)
Let's say you have a
List<Event> Events;
of your Events. You can create a simple LINQ query to get all events running at a special time with a simple method like
private IEnumerable<Event> GetRunningEvents(DateTime time)
{
return Events.Where(E => E.EventStartTime <= time && E.EventEndTime >= time);
}
Dont forget to add
using System.Linq;
to your file.
EDIT: Without LINQ a possible approach is
private List<Event> GetRunningEvents(DateTime time)
{
List<Event> RunningEvents = new List<Event>();
foreach(Event E in Events)
{
if (E.EventStartTime <= time && E.EventEndTime >= time)
{
RunningEvents.Add(E);
}
}
return RunningEvents;
}
Try Linq Where:
var list = new List<Event>();
var searchTime = DateTime.Now;
var result = list.Where(e => e.EventStartTime <= searchTime && searchTime <= e.EventEndTime).ToList();
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
private void GetTimeBetween()
{
DateTime a = new DateTime(2010, 04, 24, 13, 10, 00);
DateTime b = new DateTime(2010, 04, 25, 13, 10, 00);
Console.WriteLine(b.Subtract(a).TotalMinutes);
double d = b.Subtract(a).TotalMinutes;
}
I'm getting TotalMinutes 1440
But how can i make now that it will create a List with all the dates and time between this two dates between a and b but in jumps of 10 minutes ?
For example in the List the first item will be:
24/4/2010 13:10:00
Then ext item will be
24/4/2010 13:20:00
And so on until b
25/4/2010 13:10:00
In this format in the List or in other formats but the idea to get all the dates+time between the two given dates.
Gee, sometimes its fun to overengineer things. There is an Aggregate function which turns a list into a scalar value - lets create one that goes the other way
public static class Extensions
{
public static IEnumerable<T> Explode<T>(this T value, Func<T,T> next, Func<T,bool> limit)
{
var n = value;
while(!limit(n))
{
yield return n;
n = next(n);
}
}
}
Usage:
DateTime a = new DateTime(2010, 04, 24, 13, 10, 00);
DateTime b = new DateTime(2010, 04, 25, 13, 10, 00);
var result = a.Explode(x => x.AddMinutes(10), x => x>b).ToList();
Live example: http://rextester.com/WCGZL87983
You could loop it:
var list = new List<DateTime>();
var start = new DateTime(2010, 04, 24, 13, 10, 00);
var end = new DateTime(2010, 04, 25, 13, 10, 00);
for (DateTime date = start; date <= end; date = date.AddMinutes(10))
list.Add(date);
Try this
var start = new DateTime(2010, 04, 24, 13, 10, 00);
var end = new DateTime(2010, 04, 25, 13, 10, 00);
for (DateTime date = start; date <= end; date = date.AddMinutes(10))
{
Console.WriteLine(date.ToString("dd/mm/yyyy HH:mm:ss"));
}