@kermank/slots
Version:
A TypeScript library for handling time slots, scheduling, and timezone operations
133 lines (132 loc) • 5.16 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeOverlappingSlots = void 0;
exports.intersectSlots = intersectSlots;
exports.unionSlots = unionSlots;
exports.symmetricDifferenceSlots = symmetricDifferenceSlots;
exports.applySetOperation = applySetOperation;
const luxon_1 = require("luxon");
const slot_helpers_1 = require("./slot-helpers");
/**
* Checks if two slots overlap based on edge strategy
*/
function doSlotsOverlap(a, b, edgeStrategy = 'inclusive') {
if (edgeStrategy === 'inclusive') {
return a.start <= b.end && b.start <= a.end;
}
else {
return a.start < b.end && b.start < a.end;
}
}
/**
* Ensures input is an array of slots and merges any overlapping slots
*/
function normalizeInput(input) {
const slots = Array.isArray(input) ? input : [input];
return (0, slot_helpers_1.mergeOverlappingSlots)(slots);
}
/**
* Returns the intersection of slots
* Works with both single slots and arrays of slots
*/
function intersectSlots(a, b, options) {
const slotsA = normalizeInput(a);
const slotsB = normalizeInput(b);
const result = [];
for (const slotA of slotsA) {
for (const slotB of slotsB) {
if (!doSlotsOverlap(slotA, slotB, options.edgeStrategy))
continue;
const start = luxon_1.DateTime.max(slotA.start, slotB.start);
const end = luxon_1.DateTime.min(slotA.end, slotB.end);
// For exclusive edges, if start equals end, there's no real overlap
if (options.edgeStrategy === 'exclusive' && start.equals(end))
continue;
// Check if intersection meets minimum duration
if (options.minDuration && end.diff(start).as('milliseconds') < options.minDuration.as('milliseconds'))
continue;
result.push({
start,
end,
metadata: options.metadataMerger(slotA.metadata, slotB.metadata)
});
}
}
return (0, slot_helpers_1.mergeOverlappingSlots)(result);
}
/**
* Returns the union of slots
* Works with both single slots and arrays of slots
*/
function unionSlots(a, b, options) {
const slots = [...normalizeInput(a), ...normalizeInput(b)];
return (0, slot_helpers_1.mergeOverlappingSlots)(slots);
}
/**
* Returns slots from A with any overlapping portions from B removed
* Will split slots that partially overlap and keep non-overlapping portions
*/
const removeOverlappingSlots = (slots, slotsToRemove, options) => {
const slotsA = normalizeInput(slots);
const slotsB = normalizeInput(slotsToRemove);
let result = [...slotsA];
for (const slotB of slotsB) {
const newResult = [];
for (const slotA of result) {
if (!doSlotsOverlap(slotA, slotB, options.edgeStrategy)) {
newResult.push(slotA);
continue;
}
// Add left part if it exists and meets minimum duration
if (slotA.start < slotB.start) {
const leftPart = {
start: slotA.start,
end: slotA.start.zoneName ? slotB.start.setZone(slotA.start.zoneName) : slotB.start,
metadata: slotA.metadata
};
if (!options.minDuration || leftPart.end.diff(leftPart.start).as('milliseconds') >= options.minDuration.as('milliseconds')) {
newResult.push(leftPart);
}
}
// Add right part if it exists and meets minimum duration
if (slotA.end > slotB.end) {
const rightPart = {
start: slotA.start.zoneName ? slotB.end.setZone(slotA.start.zoneName) : slotB.end,
end: slotA.end,
metadata: slotA.metadata
};
if (!options.minDuration || rightPart.end.diff(rightPart.start).as('milliseconds') >= options.minDuration.as('milliseconds')) {
newResult.push(rightPart);
}
}
}
result = newResult;
}
return result;
};
exports.removeOverlappingSlots = removeOverlappingSlots;
/**
* Returns the symmetric difference between slots (parts that belong to only one input)
* Works with both single slots and arrays of slots
*/
function symmetricDifferenceSlots(a, b, options) {
const aMinusB = (0, exports.removeOverlappingSlots)(a, b, options);
const bMinusA = (0, exports.removeOverlappingSlots)(b, a, options);
return (0, slot_helpers_1.mergeOverlappingSlots)([...aMinusB, ...bMinusA]);
}
/**
* Applies a set operation to slots
* Works with both single slots and arrays of slots
*/
function applySetOperation(operation, a, b, options) {
switch (operation) {
case 'union':
return unionSlots(a, b, options);
case 'intersection':
return intersectSlots(a, b, options);
case 'difference':
return (0, exports.removeOverlappingSlots)(a, b, options);
case 'symmetric_difference':
return symmetricDifferenceSlots(a, b, options);
}
}