Pergunta

I have multiple date ranges. I want to check if they are overlapping in javascript. When there are only two it is easy, I use:

if(start_times1 <= end_times2 && end_times1 >= start_times2) {}

But what is the formula when there are more than 2 date ranges?

Foi útil?

Solução

You can use nested for loops with arguments

function dateRangeOverlaps(a_start, a_end, b_start, b_end) {
    if (a_start <= b_start && b_start <= a_end) return true; // b starts in a
    if (a_start <= b_end   && b_end   <= a_end) return true; // b ends in a
    if (b_start <  a_start && a_end   <  b_end) return true; // a in b
    return false;
}
function multipleDateRangeOverlaps() {
    var i, j;
    if (arguments.length % 2 !== 0)
        throw new TypeError('Arguments length must be a multiple of 2');
    for (i = 0; i < arguments.length - 2; i += 2) {
        for (j = i + 2; j < arguments.length; j += 2) {
            if (
                dateRangeOverlaps(
                    arguments[i], arguments[i+1],
                    arguments[j], arguments[j+1]
                )
            ) return true;
        }
    }
    return false;
}

Outras dicas

Here is refined version of what Paul posted:

  • Added filter and null check to allow any number of entries
  • Changed the logic so that it can be applied on an array. Eg: [{"from": value, "to": value}]
  • Adjusted overlap check to allow times having end and start as same

Script:

function dateRangeOverlaps(a_start, a_end, b_start, b_end) {
    if (a_start < b_start && b_start < a_end) return true; // b starts in a
    if (a_start < b_end   && b_end   < a_end) return true; // b ends in a
    if (b_start <  a_start && a_end   <  b_end) return true; // a in b
    return false;
}

function multipleDateRangeOverlaps(timeEntries) {
    let i = 0, j = 0;
    let timeIntervals = timeEntries.filter(entry => entry.from != null && entry.to != null && entry.from.length === 8 && entry.to.length === 8);

    if (timeIntervals != null && timeIntervals.length > 1)
    for (i = 0; i < timeIntervals.length - 1; i += 1) {
        for (j = i + 1; j < timeIntervals.length; j += 1) {
                if (
                dateRangeOverlaps(
            timeIntervals[i].from.getTime(), timeIntervals[i].to.getTime(),
            timeIntervals[j].from.getTime(), timeIntervals[j].to.getTime()
                    )
                ) return true;
            }
        }
   return false;
}

Below code comes from my project, maybe it will help you:

function dateRangeOverlaps(startDateA, endDateA, startDateB, endDateB) {

    if ((endDateA < startDateB) || (startDateA > endDateB)) {
        return null
    }

    var obj = {};
    obj.startDate = startDateA <= startDateB ? startDateB : startDateA;
    obj.endDate = endDateA <= endDateB ? endDateA : endDateB;

    return obj;
}

//storing existing dates for comparison

public multipleExistingDates=[
         {startDate:'02/03/2020 05:00:00',endDate:'02/03/2020 05:30:00'},
         {startDate:02/04/2020 05:00:00'',endDate:'02/05/2020 05:00:00'},]

/The date to be compared with existing dates to check if the new date is overlapping with existing dates/

public checkOverlappingDsates(startDate:Date, endDate:Date):boolean{
  return this.multipleExistingDates.some((elem)=>{
       return( !((moment(endDate).diff(moment(elem.startDate))) < 0 || 
              (moment(startDate).diff(moment(elem.endDate))) > 0;})

Note: If the date is overlapping, the function return true else false. Also , you would need to install moment for date comparison.

Why don't we use moment and moment-range, is it not supported across all browsers? 🤔

window['moment-range'].extendMoment(moment);

const events1 = [{
    "Date": "05/15/2021",
    "EndTime": "17:00",
    "StartTime": "16:00"
},
{
    "Date": "05/15/2021",
    "EndTime": "18:00",
    "StartTime": "17:00"
},
{
    "Date": "05/15/2021",
    "EndTime": "18:45",
    "StartTime": "17:45"
}
];

const events2 = [{
    "Date": "05/15/2021",
    "EndTime": "17:00",
    "StartTime": "16:00"
},
{
    "Date": "05/15/2021",
    "EndTime": "18:00",
    "StartTime": "17:00"
},
{
    "Date": "05/15/2021",
    "EndTime": "19:45",
    "StartTime": "18:45"
}
];

function checkOverlap(timeSegments) {

    var overlap = timeSegments
        .map(r =>
            timeSegments.filter(q => q != r).map(q =>
                moment.range(
                    moment(q.Date + " " + q.StartTime),
                    moment(q.Date + " " + q.EndTime)
                ).overlaps(
                    moment.range(
                        moment(r.Date + " " + r.StartTime),
                        moment(r.Date + " " + r.EndTime)
                    )
                )
            )
        );

    console.log(overlap.map(x => x.includes(true)).includes(true));
}

checkOverlap(events1);
checkOverlap(events2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-range/4.0.2/moment-range.js"></script>

Simply use the areIntervalsOverlapping function from date-fns, the "modern JavaScript date utility library".

You just have to pass the two dates as arguments to the function, and it will return true or false depending if the two dates overlaps or not.

Example

Check this example from their documentation:

areIntervalsOverlapping(
  { start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
  { start: new Date(2014, 0, 17), end: new Date(2014, 0, 21) }
)
//=> true

This example above returned true because the two dates overlaps. Note that the 0 number (the second argument) in Date(2014, 0, 10) represents the month of January.

You can also use this areIntervalsOverlapping function to check if other time intervals (like hours in the same day) overlaps, because in JavaScript a Date object also considers hours.

Installation

If, for example, you are using Node.js (or any framework that uses it), you just have to install date-fns with

npm install date-fns --save

And then import the desired functions inside your JavaScript code like:

import { areIntervalsOverlapping } from "date-fns";

Of course date-fns is not limited to Node.js. You can use it inside any JavaScript project.

Wouldn't be too hard to do recursively. Make a method overlap which returns the overlapping daterange for two dates. Then in your hasOverlap(list dates) method, if the list is two items, it's simple, else, return hasoverlap(overlap(dates[0], dates[1]), rest of list)

No matter the language, the basic logic to see if two date ranges overlap is:

max(range_1_start, range_2_start) <= min(range_1_end, range_2_end)

In JavaScript syntax, that might look like this:

function doDatesOverlap(start_1,end_1,start_2,end_2){
    return Math.max(start_1,start_2) <= Math.min(end_1,end_2);
}

var start_1 = new Date('01/01/2023');
var end_1 = new Date('01/31/2023');
var start_2 = new Date('01/15/2023');
var end_2 = new Date('02/15/2023');

if(doDatesOverlap(start_1,end_1,start_2,end_2)){
    console.log('They overlap!');
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top