Huuuff. A cold tightness worms its way down your spine.
Haaaaah. Your boss's frigid breath washes over your neck like an overflowing sewer. But you can’t tell them to hover somewhere else...they've been watching you like a hawk ever since the office time-tracking system broke.
Now, they're just there...hovering…breathing. Huffffff.
You tried writing your hours on a timesheet, but your boss is pretty busy with all that respiration. What if they didn't do the math right and mis-added your hours?
Here's how to take matters into your own hands with JavaScript. Haaaaa.
You have a timesheet that looks like the contents of this String
:
const a = `November 30 2023 13:20-15:00, 15:30-16:40
December 4 2023 15:45-16:15,
December 5 2023 08:00-08:30, 18:15-19:30, 21:45-22:15, 22:30-23:00
December 6 2023 19:45-21:45
December 7 2023 14:15-14:45, 15:15-15:45, 16:00-16:30, 16:45-17:15, 18:45-20:15, 20:45-22:45
December 13 2023 03:00-03:50
December 15 2023 09:00-09:30 20:30-21:15 21:45-22:30 23:30-24:00
December 16 2023 23:25-23:55
December 17 2023 19:05-19:50 20:30-21:30 22:15-23:45
December 20 2023 23:30-24:00
December 21 2023 02:20-2:50 03:15-03:30 13:40-14:00 16:00-17:15 17:45-18:45 19:20-20:10 21:30-22:20 23:00 - 24:00
December 22 2023 0:00-0:15`;
Each line contains a date followed by some time intervals. But they're not very cleanly written — some intervals are comma-separated, others have irregular spacing, etc.
Given these inconsistencies, it's probably good to assume no other guarantees. For instance, who says all the times will be from the same year? Nobody, that’s who. You want to add the hours together as robustly as possible.
This is pretty easy. We start by creating a couple of regular expressions and a helper function. Then, we’ll just…Huuuuffh!
Ugh!! Boss, go chill somewhere for a second!
As I was saying, now we can complete the task with a three-step algorithm:
We split the timesheet string into individual lines and reduce over them to create an array of objects
For each iteration, we:
A. Split the line around the year using yearRegex
, recording the date string
B. Collect all time range intervals using intervalsRegex
C. Reduce over the time ranges, parsing each as a JavaScript Date object using the parseDate
helper and the date string from step 2.A
Reduce over the array of objects to find the total hours
const yearRegex = /(\d{4})/gi;
const intervalsRegex = /(\d+\:\d+)\s*-\s*(\d+\:\d+)/gi;
const parseDate = (date, time) => Date.parse(`${date} ${time}`);
let times = [...a.split("\n")].reduce((arr, entryLine) => {
let entryLineSplit = entryLine.split(yearRegex);
let o = {
date: entryLineSplit[0] + entryLineSplit[1],
timeRanges: [...entryLine.matchAll(intervalsRegex)],
};
o.hoursForDay = o.timeRanges.reduce((total, [x, start, end]) => {
let s = parseDate(o.date, start);
let e = parseDate(o.date, end);
return total + (e - s) / 1000 / 60 / 60;
}, 0);
return [...arr, o];
}, []);
console.log(times);
let totalHours = times.reduce((total, { hoursForDay }) => total + hoursForDay, 0);
console.log(totalHours);
If you uncomment the console.log(times);
statement, you'll see an array containing objects like:
{
date: 'November 30 2023',
timeRanges: [
[
'13:20-15:00',
'13:20',
'15:00',
index: 17,
input: 'November 30 2023 13:20-15:00, 15:30-16:40',
groups: undefined
],
[
'15:30-16:40',
'15:30',
'16:40',
index: 30,
input: 'November 30 2023 13:20-15:00, 15:30-16:40',
groups: undefined
]
],
totalHours: 2.8333333333333335
},
Building an array of objects has a few distinct advantages over simply extracting the time intervals and adding them.
Date.parse
.
String.prototype.split
, which might not be unique depending on whether two days included the same time intervals.This solution isn't perfect:
times
object. To optimize this code, it’d likely be better to use a single reduce over an empty object containing totalHours
and times
as fields.
You now know how to extract time from a messy timesheet. With a little extra effort, you could even build a full-on time clock. But then you wouldn't get to spend all that nice quality time getting to know your boss, would you? Huffff.