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>.
In my app I've a ticker which runs every 5 seconds. I've also an internal clock and I want to detect when the day chages.
To test, I have tried the following code without success:
DateTime A = new DateTime(2019, 6, 20, 23, 58, 29);
DateTime B = new DateTime(2019, 6, 21, 00, 01, 12);
Int32 dd = (B-A).Days; // it returns 0
double dd = (B-A).TotalDays; // it return 0.00002136213
If I check if TotalDays > 0 I succesfully detect the day change but in the follwing case (with a difference of a minute)
DateTime C = new DateTime(2019, 6, 20, 12, 58, 29);
DateTime D = new DateTime(2019, 6, 20, 12, 59, 29);
the compare fails. Since I need to call a method when day changes, with the example above its called every time and I do not want this behevior. Any hint?
compare Date part of DateTime directly
bool isSameDay = (A.Date == B.Date);
Look at only the Date parts
DateTime A = new DateTime(2019, 6, 20, 23, 58, 29);
DateTime B = new DateTime(2019, 6, 21, 00, 01, 12);
Int32 dd = (B.Date-A.Date).Days;
For your ticker why not use TimeSpan variables to complete your comparison. You set 1 static timespan variable to 24 hours (1 day) and then create a secondary one to store the values. You then set your second timespan variable equal to the subtraction of your two days and this would let you know if a day had gone by.
`
TimeSpan newDayReference = new TimeSpan(24,0,0);
TimeSpan comparison;
//These two variables set to show difference.
DateTime A= DateTime.Now.AddDays(-1);
DateTime B = DateTime.Now;
comparison = B - A;
if(comparison > newDayReference){ //success }
`
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'm trying to workout the amount of time between two LocalDateTime values and exclude specific dates (in this example, it's bank holidays).
var bankHolidays = new[] { new LocalDate(2013, 12, 25), new LocalDate(2013, 12, 26) };
var localDateTime1 = new LocalDateTime(2013, 11, 18, 10, 30);
var localDateTime2 = new LocalDateTime(2013, 12, 29, 10, 15);
var differenceBetween = Period.Between(localDateTime1, localDateTime2, PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
The differenceBetween value shows the number of days/hours/minutes/seconds between the two dates, as you would expect.
I could check every single day from the start date and see if the bankHolidays collection contains that date e.g.
var bankHolidays = new[] { new LocalDate(2013, 12, 25), new LocalDate(2013, 12, 26) };
var localDateTime1 = new LocalDateTime(2013, 11, 18, 10, 30);
var localDateTime2 = new LocalDateTime(2013, 12, 29, 10, 15);
var differenceBetween = Period.Between(localDateTime1, localDateTime2, PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
var london = DateTimeZoneProviders.Tzdb["Europe/London"];
for (var i = 1; i < differenceBetween.Days; ++i)
{
var x = localDateTime1.InZoneStrictly(london) + Duration.FromStandardDays(i);
if (bankHolidays.Any(date => date == x.Date))
{
//subtract one day for the period.
}
}
I feel like I'm missing some obvious and there should be an easier method, is there a simpler way to find a period between two dates whilst excluding certain dates?
I also need to include weekends in this exclusion too, the obvious way seems to be to check the day of the week for weekends whilst checking bank holidays, this just doesn't seem like the best/correct way of handling it though.
I feel like I'm missing some obvious and there should be an easier method, is there a simpler way to find a period between two dates whilst excluding certain dates?
Well, it's relatively easy to count the number of bank holidays included in a date-to-date range:
Sort all the bank holidays in chronological order
Use a binary search to find out where the start date would come in the collection
Use a binary search to find out where the end date would come in the collection
Subtract one index from another to find how many entries are within that range
Work out the whole period using Period.Between as you're already doing
Subtract the number of entries in the range from the total number of days in the range
The fiddly bit is taking into account that the start and/or end dates may be bank holidays. There's a lot of potential for off-by-one errors, but with a good set of unit tests it should be okay.
Alternatively, if you've got relatively few bank holidays, you can just use:
var period = Period.Between(start, end,
PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
var holidayCount = holidays.Count(x => x >= start && x <= end);
period = period - Period.FromDays(holidayCount);
Just use TimeSpan to get the difference, all times are in your current local time zone:
var bankHolidays = new[] { new DateTime(2013, 12, 25), new DateTime(2013, 12, 26) };
var localDateTime1 = new DateTime(2013, 11, 18, 10, 30, 0);
var localDateTime2 = new DateTime(2013, 12, 29, 10, 15, 0);
var span = localDateTime2 - localDateTime1;
var holidays = bankHolidays[1] - bankHolidays[0];
var duration = span-holidays;
Now duration is your time elapsed between localDateTime1 and localDateTime2.
If you want to exlude two dates via the bankHolidays you can easiely modify the operations above.
You might use an extra method for this operation:
public static TimeSpan GetPeriod(DateTime start, DateTime end, params DateTime[] exclude)
{
var span = end - start;
if (exclude == null) return span;
span = exclude.Where(d => d >= start && d <= end)
.Aggregate(span, (current, date) => current.Subtract(new TimeSpan(1, 0, 0, 0)));
return span;
}
Now you can just use this:
var duration = GetPeriod(localDateTime1, localDateTime2, bankHolidays);
Does someone knows how to calculate the total hours between 2 times?
For example if a worker clocks in at 8:00 and out at 16:00, I would like to know that in decimal it's 8.0 hours and it's 8:00 hours.
I'm using C# framework 2.0.
The variables that hold the in and out time are of type string.
TY
DateTime start = new DateTime(2010, 8, 25, 8, 0, 0);
DateTime end = new DateTime(2010, 8, 25, 16, 0, 0);
Console.WriteLine((end - start).TotalHours);
for strings:
DateTime start = DateTime.Parse("8:00");
DateTime end = DateTime.Parse("16:00");
Console.WriteLine((end - start).TotalHours);
I came up with this daylight saving time safe method. The function is correct for both UTC and local timezones. If the DateTimeKind is Unspecified on either of the inputs then the return value is undefined (which is a fancy way of saying it could be incorrect).
private double TotalHours(DateTime earliest, DateTime latest)
{
earliest = (earliest.Kind == DateTimeKind.Local) ? earliest.ToUniversalTime() : earliest;
latest = (latest.Kind == DateTimeKind.Local) ? latest.ToUniversalTime() : latest;
return (latest - earliest).TotalHours;
}
System.DateTime punchIn = new System.DateTime(2010, 8, 25, 8, 0, 0);
System.DateTime punchOut = new System.DateTime(2010, 8, 25, 16, 0, 0);
System.TimeSpan diffResult = punchOut.Subtract(punchIn);
Check out TimeSpan.TotalHours:
TimeSpan difference = datetime2 - datetime1;
double totalHours = difference.TotalHours;
You can do it by subtracting two datetimes and using the TotalHours property of the resulting Timespan. Heres an example:
DateTime start = new DateTime(2010, 8, 25, 8, 0, 0);
DateTime end = new DateTime(2010, 8, 25, 16, 0, 0);
int hours = end.Subtract(start).TotalHours;