The MATLAB solution isn't as ugly as everyone assumes (although it could probably be simplified using the Financial Toolbox). To cut to the chase, here is the solution as a function that accepts two datetime
values:
function workMins = work_time(startTime, endTime)
dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
dateshift(endTime, 'start', 'day')).', 1, 2); %'
dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
dateBlock(:, 2) = dateBlock(:, 2)+hours(17);
dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));
end
And here's how it all works...
First, we have our starting and ending times as datetime
values:
startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');
Now we can create an N-by-2
matrix of datetimes. Each row will be a day covered by the range from startTime
to endTime
, with the times set to 0:00:00
for now:
dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
dateshift(endTime, 'start', 'day')).', 1, 2);
Now we set the first column times to 8:00:00
and the second column times to 17:00:00
:
dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
dateBlock(:, 2) = dateBlock(:, 2)+hours(17);
And now we add startTime
and endTime
as the first and last elements, respectively, clipping them to the 8:00:00
to 17:00:00
time range:
dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
Next, we remove rows with a datestr
day value of 'S'
(i.e. a weekend day):
dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
And finally, we take the column differences, sum them (ignoring negative values), and convert to minutes:
workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));
And we get the desired result:
workMins =
600
EDIT:
Regarding the new request, you can change the function slightly to allow passing in the starting and ending times of a work day like so:
function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
dateshift(endTime, 'start', 'day')).', 1, 2); %'
dateBlock(:, 1) = dateBlock(:, 1)+hours(workDayStart);
dateBlock(:, 2) = dateBlock(:, 2)+hours(workDayEnd);
...
And now you can call it with your separate work time ranges and add the results:
startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');
workTotal = work_time(startTime, endTime, 9.5, 11.5) ...
+ work_time(startTime, endTime, 13, 15);
Or subtract a subrange from a larger range:
workTotal = work_time(startTime, endTime, 9.5, 15) ...
- work_time(startTime, endTime, 11.5, 13);
If you'd prefer instead to specify the work day times as character arrays, you can write the function like this:
function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
dateshift(endTime, 'start', 'day')).', 1, 2); %'
workDayStart = datevec(workDayStart);
workDayEnd = datevec(workDayEnd);
dateBlock(:, 1) = dateBlock(:, 1)+duration(workDayStart(4:6));
dateBlock(:, 2) = dateBlock(:, 2)+duration(workDayEnd(4:6));
...
And use it like this:
workTotal = work_time(startTime, endTime, '9:30', '11:30') ...
+ work_time(startTime, endTime, '13:00', '15:00');