Насколько я понимаю, у вас есть проблема со сравнением периода времени между двумя данными точками даты / времени с несколькими отдельными периодами времени доступности между точками даты / времени, которые могут перекрываться или быть последовательными, чтобы определить, является ли требуемый период полностью покрывается одним или несколькими периодами доступности.
Имея дело с подобными проблемами в прошлом, я нашел хороший способ справиться с этим - предварительно обработать несколько отдельных периодов доступности, чтобы объединить перекрывающиеся или смежные периоды в «нормализованные» более крупные, чтобы в итоге вы получаете минимальный набор периодов доступности, охватывающий все времена, определенные в исходном наборе. Тогда становится намного проще сверять требуемый период с нормализованным набором.
Это также имеет преимущество (хотя это не указано в вашем требовании), что вы можете добавить доступность от нескольких провайдеров (например, если вы ищете непрерывное «покрытие», а не конкретный отдельный ресурс).
Вот пример кода, который иллюстрирует этот подход. Пожалуйста, будьте осторожны, этот код не полностью протестирован.
/**
* 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 ) ) ) ;