Calculate Business Hours - c#

I am using following code to calculate the buiness hours
TimeRange timeRange = new TimeRange(
new DateTime(2018, 1, 08, 10, 00, 00),
new DateTime(2018, 1, 15, 15, 30, 00));
CalendarDateDiff dateDiff = new CalendarDateDiff();
dateDiff.WorkingHours.Add(new HourRange(9, 18));
dateDiff.AddWorkingWeekDays();
var calendarDateAdd = new CalendarDateAdd();
calendarDateAdd.ExcludePeriods.Add(new Day(2018, 1, 10,calendarDateAdd.Calendar));
TimeSpan nonBusinessHours = dateDiff.Difference(timeRange.Start, timeRange.End);
double businessHours = timeRange.Duration.TotalHours -nonBusinessHours.TotalHours;
return businessHours;
The details explanation is in the code project.Calculate-Business-Hours # codeproject. In that code it specify a code to add holidays . But it not correctly working as specified.
The required Nuget package is listed here
TimePeriodLibrary.NET

Related

TimeSpan and ToString()

I want to display minutes, seconds, and milliseconds saved in TimeSpan. It should look like this:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
Console.WriteLine(d.ToString(#"mm\:ss\:fff"));
But it returns 47:30:000 which is only partially true, because it ignored one hour. I want it to be converted into minutes, not ignored.
I think you need TimeSpan.TotalMinutes.
https://learn.microsoft.com/en-us/dotnet/api/system.timespan.totalminutes?view=net-7.0
In your case it could be:
Console.WriteLine($#"{(int)d.TotalMinutes}:{d:ss\:fff}");
TimeSpan has a property for getting the total number of minutes contained in it, as a double value: TimeSpan.TotalMinutes Property.
Note that if your TimeSpan's duration is not in whole minutes you will get a fraction part:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
Console.WriteLine(d.TotalMinutes);
Output:
107.5
Note that TimeSpan has similar properties for TotalDays, TotalHours, TotalSeconds, TotalMilliseconds.
Update:
Based on the comments below, here's a solution that prints in a format splitting minutes, seconds and milliseconds:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
double totalMin = d.TotalMinutes;
int totalMinInt = (int)totalMin; // will get only the whole minutes.
Console.WriteLine(totalMinInt.ToString() + // print the whole minutes part
":" +
d.ToString(#"ss\:fff")); // print the fraction part
Output:
107:30:000

C# - Searching Time between 2 DateTime Objects of different days

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();

How can i find all the times between two given dates and time? [closed]

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"));
}

Calculating "working time" using TimePeriod.NET's CalendarPeriodCollector gives unexpected results

I'm trying to calculate a due date for a service level agreement, and at the same time, I also need to back calculate the service level agreement in the other direction.
I've been struggling with calculations for "working time" (i.e. the time that work is possible during a set of days), and decided to use a third party library called TimePeriodLibrary.NET for the task. I need to be able to do two things:
Given a start DateTime and a TimeSpan, you should receive a DateTime of when a service level agreement date is due (date due).
Given a start DateTime and an end DateTime, you should receive a TimeSpan of how long that service level agreement should take.
All source code (test project is on GitHub). I have a ServiceLevelManager class that does all the work. It take a list of WorkDays and HolidayPeriods, in order to work out which hours are available to be worked. The CalendarPeriodCollector class is giving unexpected results. The expectations that do work in determining the due date from a timespan, do not calculate correctly when I back calculate them.
Can anyone see whether I am doing something wrong, or whether the library has a bug?
namespace ServicePlanner
{
using System;
using System.Collections.Generic;
using Itenso.TimePeriod;
public class ServicePlannerManager
{
public ServicePlannerManager(IEnumerable<WorkDay> workDays, IEnumerable<HolidayPeriod> holidays)
{
this.WorkDays = workDays;
this.Holidays = holidays;
}
public IEnumerable<WorkDay> WorkDays { get; set; }
public IEnumerable<HolidayPeriod> Holidays { get; set; }
public TimeSpan GetRemainingWorkingTime(DateTime start, DateTime dueDate)
{
var filter = new CalendarPeriodCollectorFilter();
foreach (var dayOfWeek in this.WorkDays)
{
filter.CollectingDayHours.Add(new DayHourRange(dayOfWeek.DayOfWeek, new Time(dayOfWeek.StartTime), new Time(dayOfWeek.EndTime)));
}
foreach (var holiday in this.Holidays)
{
filter.ExcludePeriods.Add(new TimeBlock(holiday.StartTime, holiday.EndTime));
}
var range = new CalendarTimeRange(start, dueDate);
var collector = new CalendarPeriodCollector(filter, range);
collector.CollectHours();
var duration = collector.Periods.GetTotalDuration(new TimeZoneDurationProvider(TimeZoneInfo.FindSystemTimeZoneById("UTC")));
return duration;
//var rounded = Math.Round(duration.TotalMinutes, MidpointRounding.AwayFromZero);
//return TimeSpan.FromMinutes(rounded);
}
}
}
The Unit tests that are failing are extracted below:
[TestFixture]
public class ServicePlannerManagerTest
{
[Test, TestCaseSource("LocalSource")]
public void GetRemainingWorkingTimeWithHolidayShouldOnlyEnumerateWorkingTime(DateTime startTime, TimeSpan workingHours, DateTime expectedDueDate, string expectation)
{
// Arrange
var workDays = new List<WorkDay>
{
new WorkDay(DayOfWeek.Monday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
new WorkDay(DayOfWeek.Tuesday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
new WorkDay(DayOfWeek.Wednesday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
new WorkDay(DayOfWeek.Thursday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
new WorkDay(DayOfWeek.Friday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
};
var holidayPeriods = new List<HolidayPeriod>
{
new HolidayPeriod(new DateTime(2015, 9, 15, 00, 0, 0), new DateTime(2015, 9, 16, 0, 0, 0))
};
var service = new ServicePlannerManager(workDays, holidayPeriods);
// Act
var result = service.GetRemainingWorkingTime(startTime, expectedDueDate);
// Assert -
Assert.AreEqual(workingHours.TotalHours, result.TotalHours, expectation);
}
protected IEnumerable LocalSource()
{
yield return
new TestCaseData(
new DateTime(2015, 9, 14, 9, 0, 0),
new TimeSpan(23, 0, 0),
new DateTime(2015, 9, 17, 16, 0, 0),
"5. Expected 23 hours of working time to end on the 17/09/2015 16:00. Monday to Thursday evening. Just short of 3 full working days by one hour. Tuesday is holiday.");
}
}
Output of this test is
5. Expected 23 hours of working time to end on the 17/09/2015 16:00. Monday to Thursday evening. Just short of 3 full working days by one hour. Tuesday is holiday.
Expected: 23.0d
But was: 15.999999999944444d
I want to know if I am using the collector incorrectly, or if the collector has a bug.
This looks like a great library for solving a familiar problem.
The best thing to do is to output the periods in the period collection to help you debug the problem.
I've rewritten your test to use the base types in the examples from their documentation:
[Test, TestCaseSource("LocalSource")]
public void SO_GetRemainingWorkingTimeWithHolidayShouldOnlyEnumerateWorkingTime(DateTime startTime,
TimeSpan workingHours, DateTime expectedDueDate, string expectation)
{
CalendarPeriodCollectorFilter filter = new CalendarPeriodCollectorFilter();
filter.Months.Add(YearMonth.September); // only Januaries
filter.WeekDays.Add(DayOfWeek.Monday); //
filter.WeekDays.Add(DayOfWeek.Tuesday); //
filter.WeekDays.Add(DayOfWeek.Wednesday); //
filter.WeekDays.Add(DayOfWeek.Thursday); //
filter.WeekDays.Add(DayOfWeek.Friday); //
filter.CollectingHours.Add(new HourRange(9, 17)); // working hours
CalendarTimeRange testPeriod = new CalendarTimeRange(startTime, expectedDueDate);//new DateTime(2015, 9, 14, 9, 0, 0), new DateTime(2015, 9, 17, 18, 0, 0));
Console.WriteLine("Calendar period collector of period: " + testPeriod);
filter.ExcludePeriods.Add(new TimeBlock(new DateTime(2015, 9, 15, 00, 0, 0), new DateTime(2015, 9, 16, 0, 0, 0)));
CalendarPeriodCollector collector = new CalendarPeriodCollector(filter, testPeriod);
collector.CollectHours();
foreach (ITimePeriod period in collector.Periods)
{
Console.WriteLine("Period: " + period); // THIS WILL HELP A LOT!
}
var result = collector.Periods.GetTotalDuration(new TimeZoneDurationProvider(TimeZoneInfo.FindSystemTimeZoneById("UTC")));
Console.WriteLine(result);
//
}
This results in:
Calendar period collector of period: 14/09/2015 09:00:00 - 17/09/2015 15:59:59 | 3.06:59
Period: 14/09/2015 09:00:00 - 14/09/2015 16:59:59 | 0.07:59
Period: 16/09/2015 09:00:00 - 16/09/2015 16:59:59 | 0.07:59
15:59:59.9999998
So what I've noticed is that the very last period is missing.
If you change the end time of your period from 4PM to 6PM (and therefore expect an extra hour = 24) it will just about pass. (you will also need to round the result)
So it looks like the periods need to be completely covered by the total duration, partial coverage is not counted. You may be able to change the options of the library, alternatively you may be able to add each hour of the working day as separate CollectingHours (hacky)
Hope that gets you closer to the answer you need!

Check 30 minute interval pair and ignore if pair not found

I'm expecting the following sequence of time data in 30 minute interval from another system. I need to make this 30 minute pair in to one hour interval:
e.g.
Source Data:
Start:7:00
End: 7:30
Start:7:30
End: 8:00
Start:8:00
End: 8:30
Start:8:30
End: 9:00
Convert in to hour data like below:
Start:7:00
End: 8:00
Start:8:00
End: 9:00
If there's no match for 30 minute block this record should be ignored:
e.g
Start:7:00
End: 7:30
Start:7:30
End: 8:00
Start:8:00
End: 8:30
Start:8:30
End: 9:00
Start:9:00 <-- Ignore this
End: 9:30 <-- Ignore this
Can someone please suggest how this can be achieved?
The general idea for my solution is that you simply only take elements at indices:
0, 3, 4, 7, 8, 11, 12, ...
Or in other words - if index % 4 is 0 or 3.
Before doing that, we check if there are leftover dates at the end with count % 4.
If there are - we cut them.
This assumes that the dates will be all valid as the input you illustrated.
Here is my implementation:
public static IEnumerable<DateTime> CutTimes(IEnumerable<DateTime> source)
{
var count = source.Count();
var ignore = count % 4;
if (ignore != 0)
{
source = source.Take(count - ignore);
}
return source.Where((time, index) => index % 4 == 0 || index % 4 == 3);
}
Usage:
var times = new[]
{
new DateTime(2012, 5, 10, 7, 00, 0),
new DateTime(2012, 5, 10, 7, 30, 0),
new DateTime(2012, 5, 10, 7, 30, 0),
new DateTime(2012, 5, 10, 8, 00, 0),
new DateTime(2012, 5, 10, 8, 00, 0),
new DateTime(2012, 5, 10, 8, 30, 0),
new DateTime(2012, 5, 10, 8, 30, 0),
new DateTime(2012, 5, 10, 9, 00, 0),
new DateTime(2012, 5, 10, 9, 00, 0),
new DateTime(2012, 5, 10, 9, 30, 0)
};
var result = CutTimes(times);
foreach (var time in result)
{
Console.WriteLine(time);
}
Output:
10/05/2012 07:00:00
10/05/2012 08:00:00
10/05/2012 08:00:00
10/05/2012 09:00:00

Categories