Skip to content

Instantly share code, notes, and snippets.

@thecarlhall
Created November 6, 2023 15:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thecarlhall/5021e387dd2ab32bcd5a7f97c26253ac to your computer and use it in GitHub Desktop.
Save thecarlhall/5021e387dd2ab32bcd5a7f97c26253ac to your computer and use it in GitHub Desktop.
Find how much free time (i.e., unscheduled) there is per calendar day
/*
* User Settings
*/
// Always use current week. How many weeks past that to check?
const WEEKS_AHEAD = 0;
// Assume each day starts, ends at the same times
const WORK_START_HR = 9;
const WORK_END_HR = 18;
// Start date is adjusted to be the beginning of the same week
const WEEK_START = new Date();
WEEK_START.setDate(WEEK_START.getDate() - WEEK_START.getDay() + 1); // add 1 to move from Sunday to Monday
WEEK_START.setHours(WORK_START_HR, 0, 0, 0);
// End date is adjusted to be the end of the week beginning with WEEK_START
const WEEK_END = new Date(WEEK_START);
WEEK_END.setDate(WEEK_START.getDate() + 4); // Monday + 4 -> Friday
WEEK_END.setHours(WORK_END_HR, 0, 0, 0);
/*
* Global Constants
*/
const DAY_MS = 24 * 60 * 60 * 1000;
const MIN_SLOT_MIN = 15;
const SLOTS_PER_HR = 60 / MIN_SLOT_MIN;
const HOURS_PER_DAY = WORK_END_HR - WORK_START_HR;
const SLOTS_PER_DAY = HOURS_PER_DAY * SLOTS_PER_HR;
/*
* Functions
*/
function _workdays(earlier, later) {
const start = new Date(earlier);
start.setHours(0, 0, 0, 0);
const end = new Date(later);
end.setHours(0, 0, 0, 0);
const diff = end - start;
const diffDays = diff / DAY_MS;
var workdays = 0;
if (diffDays < 0) {
workdays = Math.ceil(diffDays) - 1;
} else {
workdays = Math.floor(diffDays) + 1;
}
return workdays;
}
function _slotIdx(eventTime, start) {
const workdays = _workdays(start, eventTime);
if (workdays < 0) {
return workdays;
}
const daysFromStart = workdays - 1;
const hoursFromStart = Math.min(
HOURS_PER_DAY,
Math.max(0, eventTime.getHours() - WORK_START_HR)
);
const offset = Math.floor(eventTime.getMinutes() / MIN_SLOT_MIN);
const startIdx =
(daysFromStart * SLOTS_PER_DAY)
+ (hoursFromStart * SLOTS_PER_HR)
+ offset;
return startIdx;
}
function _initFree(start, end) {
const workdays = _workdays(start, end);
const totalWorkHours = workdays * HOURS_PER_DAY;
const freeSlots = new Array(totalWorkHours * SLOTS_PER_HR).fill(1);
return freeSlots;
}
function _blockBusy(workSlots, weekStart, weekEnd) {
const calId = Session.getActiveUser().getEmail();
const freeBusy = Calendar.Freebusy.query({
"timeMin": weekStart.toISOString(),
"timeMax": weekEnd.toISOString(),
"items": [{
"id": calId,
"busy": "Active"
}]
});
Logger.log('Using calendar "' + calId + "' between " + weekStart + " and " + weekEnd);
for (const busy of freeBusy.calendars[calId].busy) {
const startIdx = Math.max(0, _slotIdx(new Date(busy.start), weekStart));
const endIdx = _slotIdx(new Date(busy.end), weekStart);
workSlots.fill(0, startIdx, endIdx);
}
}
function _printByDay(timeSlots, weekStart, weekEnd) {
const day = new Date(weekStart);
const workdays = _workdays(weekStart, weekEnd);
const freeStats = [];
for (var i = 0; i < workdays; i++) {
const start = i * SLOTS_PER_DAY;
const workdaySlots = timeSlots.slice(start, start + SLOTS_PER_DAY);
const freeSlots = workdaySlots.reduce((a, b) => a + b);
freeStats.push({
date: new Date(day).toDateString(),
total: HOURS_PER_DAY,
free: freeSlots / SLOTS_PER_HR
});
day.setDate(day.getDate() + 1);
}
return freeStats;
}
/*
* Main Method
*/
function calculateUnscheduledHours() {
const _start = new Date(WEEK_START);
const _end = new Date(WEEK_END);
for (var weekIdx = 0; weekIdx <= Math.max(0, WEEKS_AHEAD); weekIdx++) {
const freeSlots = _initFree(_start, _end);
_blockBusy(freeSlots, _start, _end);
const stats = _printByDay(freeSlots, _start, _end);
var free = 0;
var total = 0;
for (const stat of stats) {
free += stat.free;
total += stat.total;
Logger.log(stat.date + ": " + stat.free + "hrs @ " + ((stat.free / stat.total) * 100).toLocaleString() + "%");
}
Logger.log(free + " of " + total + "hrs free (" + ((free / total) * 100).toLocaleString() + "%)");
_start.setDate(_start.getDate() + 7);
_end.setDate(_end.getDate() + 7);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment