Search code examples
phpphp-7

Find out by how many days two date ranges overlap in PHP


I have two arbitrary date ranges, for example:

2019-01-01 - 2019-01-10
and
2019-01-06 - 2019-01-20

How do I find out in PHP, by how many days these date ranges overlap? (In the example above it's 5 days)


Solution

  • Here's a solution using DateTime and DateInterval objects:

    $range1 = '2019-01-01 - 2019-01-10';
    $range2 = '2019-01-06 - 2019-01-20';
    list($start, $end) = explode(' - ', $range1);
    $start1 = new DateTime($start);
    $end1 = new DateTime($end);
    list($start, $end) = explode(' - ', $range2);
    $start2 = new DateTime($start);
    $end2 = new DateTime($end);
    if ($end1 > $start1) {
        $overlap = $end1->diff(min($start2, $end2));
    }
    else {
        $overlap = $start1->diff(min($start2, $end2));
    }
    echo "overlap is " . ($overlap->format('%a') + 1) . " days";
    

    Output

    overlap is 5 days
    

    Demo on 3v4l.org

    Update

    Here is a more robust version of the code that allows for arbitrary overlapping of the ranges (including one being contained entirely in the other):

    function range_overlap($range1, $range2) {
        list($start, $end) = explode(' - ', $range1);
        $start = new DateTime($start);
        $end =  new DateTime($end);
        $start1 = min($start, $end);
        $end1 = max($start, $end);
        list($start, $end) = explode(' - ', $range2);
        $start = new DateTime($start);
        $end =  new DateTime($end);
        $start2 = min($start, $end);
        $end2 = max($start, $end);
        // check for special cases
        if ($start1 >= $start2 && $end1 <= $end2) {
            // range1 completely contained inside range2
            $overlap = $start1->diff($end1);
        }
        elseif ($start2 >= $start1 && $end2 <= $end1) {
            // range2 completely contained inside range1
            $overlap = $start2->diff($end2);
        }
        elseif ($end2 > $end1) {
            // range1 ends first
            $overlap = $start2->diff($end1);
        }
        else {
            // range2 ends first
            $overlap = $start1->diff($end2);
        }
        // if overlap is < 0 then there is no overlap
        $overlap_days = $overlap->invert ? 0 : ($overlap->format('%a') + 1);
        echo "overlap is $overlap_days days\n";
    }
    

    It can be called like this:

    range_overlap('2019-01-01 - 2019-01-10', '2019-01-06 - 2019-01-20'); // 5 days
    range_overlap('2019-01-01 - 2019-03-20', '2019-05-06 - 2019-04-20'); // no overlap
    range_overlap('2019-01-10 - 2019-05-20', '2019-01-01 - 2019-05-20'); // 131 days
    range_overlap('2019-01-06 - 2019-01-20', '2019-01-10 - 2019-01-01'); // 5 days
    range_overlap('2019-01-30 - 2019-01-10', '2019-01-12 - 2019-01-15'); // 4 days
    range_overlap('2019-02-01 - 2019-03-20', '2019-01-10 - 2019-02-28'); // 28 days
    

    Demo on 3v4l.org