Проверка доступности в недельном расписании, охватывающем несколько дней - PullRequest
0 голосов
/ 12 июня 2019

У меня есть еженедельные графики доступности, разработанные следующим образом:

  • Суббота - 12:00 - 00: 00
  • Воскресенье - 00:00 - 10:00
  • Четверг - 12:00 - 21:00

Этот пользователь, например, доступен только 3 дня и между указанными временными метками. У меня есть метки времени от и до, которые должны находиться между интервалами доступности, чтобы пользователь был доступен.

Моя проблема в том, что если 2 интервала в течение 2 разных дней непрерывны, как я могу проверить, что он доступен? Например, суббота и воскресенье дают непрерывный интервал доступности, как я могу проверить, что интервал, начинающийся в субботу в 22:00 и заканчивающийся в воскресенье в 02:00 (который должен быть доступен), действительно доступен?

1 Ответ

0 голосов
/ 13 июня 2019

Насколько я понимаю, у вас есть проблема со сравнением периода времени между двумя данными точками даты / времени с несколькими отдельными периодами времени доступности между точками даты / времени, которые могут перекрываться или быть последовательными, чтобы определить, является ли требуемый период полностью покрывается одним или несколькими периодами доступности.

Имея дело с подобными проблемами в прошлом, я нашел хороший способ справиться с этим - предварительно обработать несколько отдельных периодов доступности, чтобы объединить перекрывающиеся или смежные периоды в «нормализованные» более крупные, чтобы в итоге вы получаете минимальный набор периодов доступности, охватывающий все времена, определенные в исходном наборе. Тогда становится намного проще сверять требуемый период с нормализованным набором.

Это также имеет преимущество (хотя это не указано в вашем требовании), что вы можете добавить доступность от нескольких провайдеров (например, если вы ищете непрерывное «покрытие», а не конкретный отдельный ресурс).

Вот пример кода, который иллюстрирует этот подход. Пожалуйста, будьте осторожны, этот код не полностью протестирован.

/**
 * 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 ) ) ) ;
...