32

i want to add two date intervals to calculate the total duration in hours and minutes in fact i want to perform addittion as shown below:

$a = new DateTime('14:25');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1 : " . $interval1->format("%H:%I");
echo "<br />";

$c = new DateTime('08:00');
$d = new DateTime('13:00');
$interval2 = $c->diff($d);
echo "interval 2 : " . $interval2->format("%H:%I");
echo "<br />";

echo "Total interval : " . $interval1 + $interval2;

Any idea how to perform this type of interval addition to get the sum of the two intervals in total hours and minutes format in PHP

Sheikh Rahat Ali
  • 1,293
  • 8
  • 37
  • 61

4 Answers4

48

PHP has no operator overloading* so + with objects makes PHP trying it to convert them to string first, but DateInterval does not support that:

interval 1: 03:05
interval 2: 05:00
Total interval : 08:05

Instead you need to create a new DateTime object, then use the add function to add the intervals and finally display the difference to the reference point:

$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
echo "Total interval : ", $f->diff($e)->format("%H:%I"), "\n";

Full Example/(Demo):

$a = new DateTime('14:25');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1: ", $interval1->format("%H:%I"), "\n";

$c = new DateTime('08:00');
$d = new DateTime('13:00');
$interval2 = $c->diff($d);
echo "interval 2: ", $interval2->format("%H:%I"), "\n";

$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
echo "Total interval : ", $f->diff($e)->format("%H:%I"), "\n";

You might also want to consider looking how DateInterval stores its' values and then extend from it to do the calculation your own. The following example (Demo) is rough, it does not take into account the inverted thingy, it does not (re)set $days to false and I have not checked/tested the ISO specification of the period specifier on creation but I think it is enough to show the idea:

class MyDateInterval extends DateInterval
{
    /**
     * @return MyDateInterval
     */
    public static function fromDateInterval(DateInterval $from)
    {
        return new MyDateInterval($from->format('P%yY%dDT%hH%iM%sS'));
    }
                                  
    public function add(DateInterval $interval)
    {
        foreach (str_split('ymdhis') as $prop)
        {
            $this->$prop += $interval->$prop;
        }
    }
}

$a = new DateTime('14:25');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1: ", $interval1->format("%H:%I"), "\n";

$c = new DateTime('08:00');
$d = new DateTime('13:00');
$interval2 = $c->diff($d);
echo "interval 2: ", $interval2->format("%H:%I"), "\n";
                                  
$e = MyDateInterval::fromDateInterval($interval1);
$e->add($interval2);
echo "Total interval: ", $e->format("%H:%I"), "\n";

* If you write a PHP extension, it actually is possible (at least sort-of).

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Some more experiments: http://codepad.viper-7.com/Lh2DtL ([gist](https://gist.github.com/3142405)) – hakre Jul 19 '12 at 09:12
  • 1
    Hi, what if, with your solution, the total is greater than 60 seconds, 60 minutes, 24 hours, ... etc ? :) – Talus Jan 25 '13 at 14:55
  • @Talus: See the gist, not that it is perfect, but it shows how you can handle that. – hakre Jan 25 '13 at 23:22
  • Yup, I was refering to the last snippet (the extension of the `DateInterval`) you posted. But the first snippet (when you add / substract the intervals and then calculated the diff) was what I did, and this works pretty well :) – Talus Feb 09 '13 at 15:38
  • My answer is not totally correct, for example with some DateTime classes you can actually use the + operator. – hakre Feb 09 '13 at 16:17
  • 2
    I used this for now and created a request for the PHP core to add a new method to the DateInterval class: https://github.com/php/php-src/pull/390 – SimonSimCity Jul 28 '13 at 09:48
  • Wow, that's pretty nice Simon! Thanks for all your efforts. – hakre Jul 28 '13 at 10:53
  • I basically did what you showed in the second code block. But pumped a `\DateInterval('P135DT15H14M')` type object into the add method. Since the intervals I am working with are not date specific, this worked perfectly for solving my problem of getting total hours between two of those styles of date intervals (these are cumulative hours). Thanks! – Patrick Oct 11 '17 at 16:30
  • Shouldn't ``return new DateInterval()...`` format be ``P%yY%mM%dDT%hH%iM%sS``? The provided format misses months... – thedom Feb 11 '18 at 19:32
9

This function allows you to combine any number of DateIntervals

/**
 * Combine a number of DateIntervals into 1 
 * @param DateInterval $...
 * @return DateInterval
 */
function addDateIntervals()
{
    $reference = new DateTimeImmutable;
    $endTime = clone $reference;

    foreach (func_get_args() as $dateInterval) {
        $endTime = $endTime->add($dateInterval);
    }

    return $reference->diff($endTime);
}
dave1010
  • 15,135
  • 7
  • 67
  • 64
  • This didn't work for me, `$endTime` I think because of the `DateTimeImmutable`. Worked when I changed `new DateTimeImmutable` for `new DateTime()` – Gyfis May 11 '15 at 10:13
  • 1
    @Gyfis Strange, it works just fine for me. Remember that Immutables will return a new instance when changed. – David Yell Jan 16 '17 at 15:20
1
function compare_dateInterval($interval1, $operator ,$interval2){
    $interval1_str = $interval1->format("%Y%M%D%H%I%S");
    $interval2_str = $interval2->format("%Y%M%D%H%I%S");
    switch($operator){
        case "<":
            return $interval1 < $interval2;
        case ">":
            return $interval1 > $interval2;
        case "==" :
            return $interval1 == $interval2;
        default:
            return NULL;
    }
}
function add_dateInterval($interval1, $interval2){
    //variables
    $new_value= [];
    $carry_val = array(
                    's'=>['value'=>60,'carry_to'=>'i'],
                    'i'=>['value'=>60,'carry_to'=>'h'],
                    'h'=>['value'=>24,'carry_to'=>'d'],
                    'm'=>['value'=>12,'carry_to'=>'y']
                );

    //operator selection
    $operator = ($interval1->invert == $interval2->invert) ? '+' : '-';

    //Set Invert
    if($operator == '-'){
        $new_value['invert'] = compare_dateInterval($interval1,">",$interval2)?$interval1->invert:$interval2->invert;
    }else{
        $new_value['invert'] = $interval1->invert;
    }

    //Evaluate
    foreach( str_split("ymdhis") as $property){
        $expression = 'return '.$interval1->$property.' '.$operator.' '.$interval2->$property.';';
        $new_value[$property] = eval($expression);
        $new_value[$property] = ($new_value[$property] > 0) ? $new_value[$property] : -$new_value[$property];
        }

    //carry up
    foreach($carry_val as $property => $option){
        if($new_value[$property] >= $option['value']){
            //Modulus
            $new_value[$property] = $new_value[$property] % $option['value'];
            //carry over
            $new_value[$option['carry_to']]++;
        }
    }

    $nv = $new_value;
    $result = new DateInterval("P$nv[y]Y$nv[m]M$nv[d]DT$nv[h]H$nv[i]M$nv[s]S");
    $result->invert = $new_value['invert'];
    return $result;
}

$a = new DateTime('00:0');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1: ", $interval1->format("%H:%I"), "<br>";

$c = new DateTime('08:01:00');
$d = new DateTime('13:30:33');
$interval2 = $c->diff($d);
echo "interval 2: ", $interval2->format("%H:%I"), "<br>";

$addition = add_dateInterval($interval1,$interval2);
echo "<pre>";
echo var_dump($addition);
echo "</pre>";
0

I had the same situation. I solve it for my case by creating a new stdClass, this object emulates a DateInterval object, however it is not a real DateInterval object. Then I looping over every real DateInterval while performing an compound assignment operation (for example +=) on the desired property. See below:

$dateTimeA = new DateTime('-10 day');
$dateTimeB = new DateTime('-8 day');
$dateTimeC = new DateTime('-6 day');
$dateTimeD = new DateTime('-4 day');

$intervalA = date_diff($dateTimeA, $dateTimeB);
$intervalB = date_diff($dateTimeC, $dateTimeD);

$intervalC = new StdClass; // $intervalC is an emulation of DateInterval 
$intervalC->days = 0;

foreach([$intervalA, $intervalB] as $interval) {
    $intervalC->days += (int)$interval->format('%d');
}


var_dump($intervalC);
/*
object(stdClass)[7]
  public 'days' => int 4
*/
Julian
  • 4,396
  • 5
  • 39
  • 51