UNPKG

@kermank/slots

Version:

A TypeScript library for handling time slots, scheduling, and timezone operations

133 lines (132 loc) 5.16 kB
"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); } }