cbronson cbronson - 6 months ago 8
Javascript Question

Find available days within a date range

Let's say there's a system that shows date availability for events. We have a main date range where all events go. Events can be date ranges themselves too.


[Date X]========================================[Date Y]
[A]=====[A] [B]=====[B][C]=====[C]
[ Event A ][ Open ][ Event B ][ Event C ]

Where Date X and Date Y are the main date range where events go. And A,B, and C are events that have been scheduled.

How can I efficiently retrieve the open date range?

Example 2:

var rangeStart = new Date("04-01-2016");
var rangeEnd = new Date("04-31-2016");

var eventAStart = new Date("04-01-2016");
var eventAEnd = new Date("04-06-2016");

var eventBStart = new Date("04-15-2016");
var eventBEnd = new Date("04-30-2016");

I need to return something like:

var availableRangeStart = "04-07-2015";
var availableRangeEnd = "04-14-2016";

because these are the dates in the main range that are not overlapped by "event" ranges.

To be exact on what I am trying to do:

My app is a trip planner where the user sets the dates for their trip and then adds different destinations to that trip that have their own dates. (User is going on a trip to Europe April 1st to April 30th, they will be in Paris on April 1st to April 6, then they will be in London April 15th to April 30th). But the user has not planned anything from April 7th to April 14th. I am trying to return these dates so that when they add a new destination, the dates are pre-filled.


Here is a solution that returns from/to periods that are free:

// Helper function
function addDays(date, days) {
    return new Date(date.getTime() + days * 24*60*60*1000);

// Main function
function gaps(period, events) {
    events = events.slice(0).filter(function (a) {
        // Exclude events which are outside the main period
        return a.to >= period.from && a.from <= period.to;
    }).sort(function (a, b) {
        // Sort events by their starting date
        return a.from - b.from;
    var result = events.reduce(function (result, curr) {
        if (curr.from - result.free > 0) {
            // gap found
                from: result.free, 
                to:   addDays(curr.from, -1)
        if (curr.to - result.free >= 0) {
            // first free day is after this event
            result.free = addDays(curr.to, 1)
        return result;
    }, { gaps: [], free: period.from } );
    // Potentially add gap between last event end period-end
    if (period.to - result.free >= 0) {
            from: result.free,
            to:   period.to
    return result.gaps;

// Sample data:

var period = {
    from: new Date('2016-01-01'),
    to: new Date('2016-12-31')

var events = [
    { from: new Date('2016-02-01'), to: new Date('2016-02-29') },
    { from: new Date('2016-03-01'), to: new Date('2016-03-15') },
    { from: new Date('2016-04-16'), to: new Date('2016-04-30') },

// Call to function
var res = gaps(period, events);

// Output in snippet
document.write('<pre>' + JSON.stringify(res, null, 4));