在跨多个天的每周计划中检查可用性

时间:2019-06-12 17:40:56

标签: javascript node.js date datetime timestamp

我的每周可用性计划设计如下:

  • 星期六-12:00-00:00
  • 周日-00:00-10:00
  • 星期四-12:00-21:00

例如,此用户仅在3天之内以及指定的时间戳之间可用。我拥有的时间戳记和时间戳记之间的时间间隔必须介于用户可用的时间间隔之间。

我的问题是,如果连续2天不同的2个间隔是连续的,我如何检查它是否可用?例如,周六和周日产生一个连续的可用性间隔,我如何检查从周六22:00开始到周日02:00结束(应该可用)的时间间隔?

1 个答案:

答案 0 :(得分:0)

据我了解,您遇到的问题是将两个给定日期/时间点之间的时间段与可能重复或连续的日期/时间点之间的多个单独的可用性时间段进行比较,以查看所需的时间段是否为完全涵盖一个或多个可用期。

过去,在处理类似问题时,我发现一种解决此问题的好方法是对多个单独的可用性时段进行预处理,以便将重叠或连续的时段合并为“规范化”的较大时段,以便您最终将获得一组最少的可用性周期,这些可用性周期涵盖了原始组中定义的所有时间。然后,对照标准化集检查所需时间就变得容易得多。

这还有一个优势(尽管您的要求中没有说明),您可以从多个提供程序中添加可用性(例如,如果您正在寻找连续的“覆盖”而不是特定的单个资源)。

这里有一些示例代码说明了这种方法。请注意,此代码尚未经过全面测试。

/**
 * A class definition for an availability period
 * @param {Date} start date/time 
 * @param {Date} end date/time
 */
var Period = function( start, end ){
    // Set the start and end dates
    this.start = start;
    this.end = end;

    // Set the start and end timestamps (for faster comparison)
    this.startTime = start.getTime();
    this.endTime = end.getTime();

    // Flag to indicate if this availability period is completely contained within another
    this.contained = false;

    // Check validity of start and end
    if( this.startTime > this.endTime ){
        throw new Error( "Start time of period cannot be later than end time");
    }

    // Function to check if this period includes a required period
    this.includes = function( period ){
        if( period.startTime >= this.startTime && period.endTime <= this.endTime ) return true;
        return false;
    }

}

/**
 * A class definition for a period store that accepts addition
 * of multiple available periods and merges overlapping periods,
 * and has a function to check if a specified period is available
 */
var PeriodStore = function(){
    this.periods = [];
    this.calllevel = 0;

    // Member function to add a new period to the collection
    // of periods - if it overlaps with an existing period
    // both will be merged into a single spanning period.
    this.addAvailabilityPeriod = function( newPeriod ){
        // wind up the call count (see below)
        this.calllevel++;

        let self = this;

        newPeriod.contained = false;

        // compare the new period to all the existing periods
        self.periods.forEach( function( oldPeriod ){

            // only check if both periods are not contained in another
            if ( !newPeriod.contained && !oldPeriod.contained ){


                if( oldPeriod.includes( newPeriod ) ){
                    newPeriod.contained = true;
                } else
                if( newPeriod.includes( oldPeriod ) ){
                    oldPeriod.contained = true;
                } else
                if( oldPeriod.startTime <= newPeriod.endTime 
                    && oldPeriod.startTime > newPeriod.startTime 
                    && oldPeriod.endTime > newPeriod.endTime ){

                    // replace the two periods with a new one spanning both
                    newPeriod.contained = true;
                    oldPeriod.contained = true;
                    // Recursive call to add the new merged period to the collection
                    self.addAvailabilityPeriod( new Period(newPeriod.start, oldPeriod.end) );

                } else 
                if( newPeriod.startTime <= oldPeriod.endTime 
                    && newPeriod.startTime > oldPeriod.startTime 
                    && newPeriod.endTime > oldPeriod.endTime ){

                    // replace the two periods with a new one spanning both
                    newPeriod.contained = true;
                    oldPeriod.contained = true;

                    // Recursive call to add the new merged period to the collection
                    self.addAvailabilityPeriod( new Period(oldPeriod.start, newPeriod.end) )

                }
            }
        } )

        // If the new period is not contained within 
        // another period, then add it to the periods 
        // collection
        if( !newPeriod.contained ) self.periods.push( newPeriod );

        // unwind the call count
        this.calllevel--;

        // Clean up the list to remove any previously-existing periods that
        // are now contained withing any new periods.
        if (this.calllevel == 0 ){
            for( var ix = self.periods.length - 1; ix >= 0; ix-- ){
                if( self.periods[ix].contained ){
                    console.log( "Removing contained period", self.periods[ix] )
                    self.periods.splice( ix, 1 );
                }
            }
        }

    }

    // Function to check if a given period is contained
    // within any of the available periods in the store
    // and return the containing period if so, null if not
    this.checkAvailability = function( checkperiod ){
        var self = this;

        console.log( "Checking availability", checkperiod );
        for( var ix = 0; ix < self.periods.length ; ix++ ){

            if( self.periods[ix].includes( checkperiod ) ){
                return self.periods[ix];
            }
        }
        return null;

    }

}

// ------------------ TESTING -------------------------


// Create array of available periods
var availablePeriods = [
    new Period( new Date( "2017-08-01T07:00:00Z"),  new Date( "2017-08-01T08:00:00Z" ) )
    , new Period( new Date( "2017-08-02T07:00:00Z"), new Date( "2017-08-02T08:00:00Z" ) )
    , new Period( new Date( "2017-08-01T08:00:00Z"), new Date( "2017-08-02T06:55:00Z" ) )
]

// Create the period store
var periodStore = new PeriodStore();

// Add all the available periods to the store
availablePeriods.forEach( function(period){

    periodStore.addAvailabilityPeriod( period );

})

// Report the available periods
console.log( periodStore );

// Check availability between two date / times
var fm = new Date( "2017-08-01T07:30:00Z" );
var to = new Date( "2017-08-01T08:30:00Z" );

console.log( periodStore.checkAvailability( new Period( fm, to ) ) );

to = new Date( "2017-08-02T09:30:00Z")

console.log( periodStore.checkAvailability( new Period( fm, to ) ) ) ;