5

I just asked this question and it was marked as already answered but whoever did that obviously did not understand the question.

This post was marked as a duplicate question to this,

How to get time difference in minutes in PHP ,

but they are not even remotely the same.

I'm looking for the amount of overlapping time in two DatePeriods not the difference between two dates.

A "DatePeriod" consists of two dates and I'm looking for the amount of overlap between two DatePeriods.

There is another post I found that answers half of my question here,

Determine Whether Two Date Ranges Overlap

and it works great but it does not give me the amount of time overlap between the two date ranges. It only determines if they overlap or not. I need to know the amount of minutes that they overlap. So Here is my original question...

I assumed there would be a very easy way to do this but I have looked all over and can not find a solution. The logic is simple: find the amount of overlapping minutes given two DatePeriods.

For example,

-I have $startDate1 and $endDate1 as the first date range.

-I have $startDate2 and $endDate2 as the second date range.

(For demonstration sake, the format is 'Y-m-d H:i'. )

  • Suppose $startDate1 = '2015-09-11 09:15' and $endDate1 = '2015-09-13 11:30'.

  • Suppose $startDate2 = '2015-09-13 10:45' and $endDate2 = '2015-09-14 12:00'.

I would expect a result of 45 minutes overlap. How can this be achieved?

I have seen this done using two DateTimes but that is not what I'm looking for.

A datePeriod consists of two DateTimes and I'm looking for the difference between two DatePeriods.

Community
  • 1
  • 1
Luke James
  • 121
  • 13
  • The overlap needs to be calculated in both directions. What if $startDate1 and $endDate1 are later than or between $startDate2 and $endDate2. – Luke James Oct 21 '15 at 23:13
  • No, it doesn't. Like I said, there are four steps. (1) Calculate the later start date; overlaps are impossible before that. (2) Calculate the earlier end date; overlaps are impossible after that. (3) Subtract the later start date from the earlier end date. (4) Check the result. If it's zero or greater, there's your answer. If it's negative, then your answer is zero; that implies that the second period didn't start until the first one finished. – elixenide Oct 21 '15 at 23:17
  • I see what you mean. Thank you. I will give this a try and provide an update as to whether or not I succeeded. – Luke James Oct 21 '15 at 23:21

4 Answers4

12

As I explained in the comments, there are four steps.

  1. Calculate the later start date; overlaps are impossible before that.
  2. Calculate the earlier end date; overlaps are impossible after that.
  3. Subtract the later start date from the earlier end date.
  4. Check the result. If it's zero or greater, there's your answer. If it's negative, then your answer is zero; that implies that the second period didn't start until the first one finished.

Here's a function to do what you want:

/**
 * What is the overlap, in minutes, of two time periods?
 *
 * @param $startDate1   string
 * @param $endDate1     string
 * @param $startDate2   string
 * @param $endDate2     string
 * @returns int     Overlap in minutes
 */
function overlapInMinutes($startDate1, $endDate1, $startDate2, $endDate2)
{
    // Figure out which is the later start time
    $lastStart = $startDate1 >= $startDate2 ? $startDate1 : $startDate2;
    // Convert that to an integer
    $lastStart = strtotime($lastStart);

    // Figure out which is the earlier end time
    $firstEnd = $endDate1 <= $endDate2 ? $endDate1 : $endDate2;
    // Convert that to an integer
    $firstEnd = strtotime($firstEnd);

    // Subtract the two, divide by 60 to convert seconds to minutes, and round down
    $overlap = floor( ($firstEnd - $lastStart) / 60 );

    // If the answer is greater than 0 use it.
    // If not, there is no overlap.
    return $overlap > 0 ? $overlap : 0;
}

Sample uses:

$startDate1 = '2015-09-11 09:15';
$endDate1 = '2015-09-13 11:30';
$startDate2 = '2015-09-13 10:45';
$endDate2 = '2015-09-14 12:00';

echo overlapInMinutes($startDate1, $endDate1, $startDate2, $endDate2) . "\n";
// echoes 45

$startDate1 = '2015-09-11 09:15';
$endDate1 = '2015-09-13 11:30';
$startDate2 = '2015-09-13 12:45';
$endDate2 = '2015-09-14 12:00';

echo overlapInMinutes($startDate1, $endDate1, $startDate2, $endDate2) . "\n";
// echoes 0
elixenide
  • 44,308
  • 16
  • 74
  • 100
  • This works beautifully. Thank you very much Ed. I still don't fully understand the logic but I tested it many different ways and it works great. You must be a genius. – Luke James Oct 22 '15 at 00:01
  • solid answer - yet why are you not using 2 DatePeriod objects? – Gung Foo Aug 03 '16 at 18:34
  • @GungFoo Thanks! I may be missing something, but I'm not sure how I would use `DatePeriod`s here. As I understand it, those are for calculating recurring intervals. I'm not seeing how we could use them to determine the number of minutes of overlap in two different windows of time. – elixenide Aug 03 '16 at 18:40
  • we would reduce the number of arguments to the function to 2 (or possibly 1, if we extend DatePeriod) and at the same time make the function more generic as it would return an interval from which one could choose the format of the output. `DateInterval $myPeriod->overlapsWithPeriod((DatePeriod) $period);` – Gung Foo Aug 03 '16 at 19:02
  • @GungFoo I suppose that would work, but I didn't think it was necessary here. Good idea, though! – elixenide Aug 03 '16 at 19:27
  • There, i fixed it. :) – Gung Foo Aug 03 '16 at 19:54
2

Using @Ed Cottrell's code as a basis, i put together this little method based on PHP's OO Date classes:

class DatePeriodHelper extends DatePeriod {
    /**
     * What is the overlap, in minutes, of two time periods?
     * Based on a stackoverflow answer by Ed Cottrell - Thanks Ed :)
     * http://stackoverflow.com/questions/33270716/return-the-amount-of-overlapping-minutes-between-to-php-dateperiods
     * 
     * @param DatePeriod $period    The DatePeriod to compare $this to.
     * @return DateInterval        A DateInterval object expressing the overlap or (if negative) the distance between the DateRanges
     */
    public function overlapsWithPeriod(DatePeriod $period) {
        // Figure out which is the later start time
        $lastStart = $this->getStartDate() >= $period->getStartDate() ? $this->getStartDate() : $period->getStartDate();

        // Figure out which is the earlier end time
        $firstEnd = $this->getEndDate() <= $period->getEndDate() ? $this->getEndDate() : $period->getEndDate();

        // return a DateInterval object that expresses the difference between our 2 dates
        return $lastStart->diff($firstEnd);
    }
}

Using Ed's test cases this yields:

$interval = new DateInterval('P1M');

$startDate1 = new DateTime('2015-09-11 09:15');
$endDate1 = new DateTime('2015-09-13 11:30');
$startDate2 = new DateTime('2015-09-13 10:45');
$endDate2 = new DateTime('2015-09-14 12:00');

$interval = new DateInterval('P1M');

$period1 = new DatePeriodHelper($startDate1, $interval, $endDate1);
$period2 = new DatePeriodHelper($startDate2, $interval, $endDate2);

$overlap = $period1->overlapsWithPeriod($period2);
echo $overlap->format('%r%h:%i:%s');
// echoes: 0:45:0

$startDate2 = new DateTime('2015-09-13 12:45');
$endDate2 = new DateTime('2015-09-14 12:00');
$period2 = new DatePeriodHelper($startDate2, $interval, $endDate2);

$overlap = $period1->overlapsWithPeriod($period2);
echo $overlap->format('%r%h:%i:%s');
// echoes -1:15:0
Gung Foo
  • 13,392
  • 5
  • 31
  • 39
1

If you have $start1, $end1, $start2, $end2 as Unix-Timestamps (e. g. created with strtotime()) you can simply type:

$overlap = max(min($end1, $end2) - max($start1, $start2), 0);

The result is in seconds. It is trivial to convert it to hours/days etc.

Peter
  • 2,051
  • 1
  • 15
  • 20
0

I wrote something like this for my website. Here's the code. It's not as compact as some of the other answers, but hopefully it's fairly readable.

// Input should be UNIX timestamps
function get_minutes_of_overlap($date1_start_time, $date1_end_time, $date2_start_time, $date2_end_time) {
    if ( $date1_end_time == $date2_end_time ) {
        if ( $date1_start_time > $date2_start_time ) {
            $date1_ends_later = TRUE;
        } else {
            $date1_ends_later = FALSE;
        }
    } elseif ( $date1_end_time > $date2_end_time ) {
        $date1_ends_later = TRUE;
    } else {
        $date1_ends_later = FALSE;
    }

    if ( $date1_ends_later ) {
        $minutes_of_overlap = ($date2_end_time - $date1_start_time) / 60;
    } else {
        $minutes_of_overlap = ($date1_end_time - $date2_start_time) / 60;
    }

    return $minutes_of_overlap;
}
RedDragonWebDesign
  • 1,797
  • 2
  • 18
  • 24