I am wondering if anyone can help me, I have a collection of start times and end times for a work shift of a day. the hours can be spread across the day. I am trying group by the hour in day (like 0, 1, 2...23, 24) and show that hour in intervals of 10 minutes or worked or not worked. So, I would like to get the end result like below:
I want to able distinguish between worked and not on a hourly basis, the input provides the worked but I have calculate the not worked, I created a method for handling if a time falls outside a 10 minute interval it will set to the nearest one. Method called DoRounding:
Example:
9 am => 0 - 10 Worked
10 - 20 Not Worked
20 - 30 Worked
30 - 40 Worked
40 - 50 Worked
50 - 60 Worked
The time that fails out of the period can be be handled like so
private static int DoRounding(DateTime date)
{
if (Enumerable.Range(0, 10).Contains(date.Minute))
return 0;
if (Enumerable.Range(10, 20).Contains(date.Minute))
return 20;
if (Enumerable.Range(20, 30).Contains(date.Minute))
return 30;
if (Enumerable.Range(30, 40).Contains(date.Minute))
return 40;
if (Enumerable.Range(40, 50).Contains(date.Minute))
return 50;
return 60;
}
My method to explode the workblock (I was trying to break down the work period into hours here so I could add the missing parts in another method)
public static IEnumerable<Tuple<int, DateTime>> CalculateIntervals(WorkPeriod workBlock)
{
yield return new Tuple<int, DateTime>(workBlock.StartTime.Hour, workBlock.StartTime);
var dateTime = new DateTime(workBlock.StartTime.Year, workBlock.StartTime.Month, workBlock.StartTime.Day, workBlock.StartTime.Hour, workBlock.StartTime.Minute, 0, workBlock.StartTime.Kind).AddHours(1);
while (dateTime < workBlock.EndTime)
{
yield return new Tuple<int, DateTime>(dateTime.Hour, dateTime);
dateTime = dateTime.AddHours(1);
}
yield return new Tuple<int, DateTime>(workBlock.EndTime.Hour, workBlock.EndTime);
}
My attempt at grouping (I want to group into the time slots here to the hour and the intervals such as 1 pm, 0 - 10 minutes and mark it as worked but if an interval was missing from here add it as not worked)
public static void WorkingHourIntervalStrings(List<WorkPeriod> WorkingHours)
{
var output = new List<Tuple<int, DateTime>>();
foreach (var result in WorkingHours.Select(CalculateIntervals))
output.AddRange(result);
output = output.OrderBy(x => x.Item2).ToList();
var test = output.GroupBy(
p => p.Item1,
p => p.Item2.Minute,
(key, g) => new { Worked = key, Minute = g.ToList() });
}
Class
public class WorkPeriod
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
Calling
var input = new List<WorkPeriod>
{
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 9, 40, 56), EndTime = new DateTime(2020, 5, 25, 14, 22, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 9, 50, 56), EndTime = new DateTime(2020, 5, 25, 14, 59, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 13, 40, 56), EndTime = new DateTime(2020, 5, 25, 18, 22, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 19, 40, 56), EndTime = new DateTime(2020, 5, 25, 23, 22, 12) }
};
TimeIntervals.WorkingHourIntervalStrings(input);
Possible output structure:
public class Interval
{
public Interval() => Contents = new List<Contents>();
public int Hour { get; set; }
public List<Contents> Contents { get; set; }
}
public class Contents
{
public bool Worked { get; set; }
public int Start { get; set; }
public int End { get; set; }
}
Based on your above explanations I would do the following:
public class Interval
{
public Interval() => Contents = new List<Contents>();
public int Hour { get; set; }
public List<Contents> Contents { get; set; }
}
public class Contents
{
public bool Worked { get; set; }
public int Start { get; set; }
//public int End { get; set; }
public int End => Start + 10;
}
public class WorkPeriod
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
Look at the Contents class. The End
property is autocalculated from the Start
one.
Then I would create the following Calculator
class:
public class Calculator
{
public bool[] WorkedIntervals = new bool[24 * 6];
private void SetWork(int Hour, int Min)
{
int pos = Hour * 6 + Min / 10;
WorkedIntervals[pos] = true;
}
private void UpdateIntervals(WorkPeriod period)
{
var cur = period.StartTime;
while (cur < period.EndTime)
{
SetWork(cur.Hour, cur.Minute);
cur = cur.AddMinutes(10);
}
}
private void UpdateIntervals(List<WorkPeriod> periods)
{
foreach (var l in periods)
UpdateIntervals(l);
}
public IEnumerable<Interval> CalcIntervals(List<WorkPeriod> periods)
{
var minTime = (from p in periods
select p.StartTime).Min();
var maxTime = (from p in periods
select p.EndTime).Max();
UpdateIntervals(periods);
for(int h=minTime.Hour; h<=maxTime.Hour; ++h)
{
int pos = h * 6;
var intrvl = new Interval() { Hour = h };
for (int m=0; m<=5; m++)
{
if (WorkedIntervals[pos + m])
intrvl.Contents.Add(new Contents() { Start = m * 10, Worked = true });
else
intrvl.Contents.Add(new Contents() { Start = m * 10, Worked = false });
}
yield return intrvl;
}
}
}
The idea is that you have to flatten all your time intervals to an array of 144 boolean values (24*6) that represents if each of this 10 minute time interval has been worked or not. eg. if the 7th index of the array is true then it means that at Hour 1 (hour 0 is in indexes 0-5) the 10-20 min interval has been worked.
Then, on your main function you do the following.
var input = new List<WorkPeriod>
{
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 9, 40, 56), EndTime = new DateTime(2020, 5, 25, 14, 22, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 9, 50, 56), EndTime = new DateTime(2020, 5, 25, 14, 59, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 13, 40, 56), EndTime = new DateTime(2020, 5, 25, 18, 22, 12) },
new WorkPeriod { StartTime = new DateTime(2020, 5, 25, 19, 40, 56), EndTime = new DateTime(2020, 5, 25, 23, 22, 12) }
};
Calculator ints = new Calculator();
var res = ints.CalcIntervals(input).ToList();
The res
list should contain the hour-intervals from the minimum StartTime
to the maximum EndTime
with their respected sub-lists.