PHP 将两个 DateInterval 对象相加

PHP adding two DateInterval-objects together

我正在使用 PHP 处理时间记录实验中的一些数字。如何将两个 DateInterval 对象加在一起?

其中一个 DateIntervals 被发现(两个 DateTime 对象之间的差异):

$start_stamp = new \DateTime( '2017-09-01T07:10' ); // DateTime-object
$end_stamp   = new \DateTime( '2017-09-01T07:10' ); // DateTime-object
$duration    = $start_stamp->diff( $end_stamp ); // DateInterval-object

另一个 DateInterval 简单定义为:

$abc = new \DateInterval( 'PT1H' ); // DateInterval with duration of 1 hour

我读过几本 places,其中一本应该将我的 DateIntervals 转换为 DateTime 对象,然后使用 $dateTimeObject->add(...) 函数。但这对我来说似乎很愚蠢。这是一个持续时间,- 所以为什么我要把它转换成它不是的东西,以便正确处理它。

我写了下面的函数来解决这个问题,- 但它看起来很笨拙。它可以写得更好吗?或者是否有更好的方法来总结两个 DateInterval 对象?

function sumDateIntervals($dateinterval_one, $dateinterval_two)
{
  $d1_years = $dateinterval_one->y;
  $d1_months = $dateinterval_one->m;
  $d1_days = $dateinterval_one->d;
  $d1_hours = $dateinterval_one->h;
  $d1_minutes = $dateinterval_one->i;
  $d1_seconds = $dateinterval_one->s;
  $d1_float = $dateinterval_one->f;

  $d2_years = $dateinterval_two->y;
  $d2_months = $dateinterval_two->m;
  $d2_days = $dateinterval_two->d;
  $d2_hours = $dateinterval_two->h;
  $d2_minutes = $dateinterval_two->i;
  $d2_seconds = $dateinterval_two->s;
  $d2_float = $dateinterval_two->f;

  $return_dateinterval = new DateInterval( 
                     'P' . ( string ) ( $d1_years + $d2_years ) . 'Y' 
                     . ( string ) ( $d1_months + $d2_months ) . 'M' 
                     . ( string ) ( $d1_days + $d2_days ) . 'DT' 
                     . ( string )  ($d1_hours + $d2_hours ) . 'H' 
                     . ( string ) ( $d1_minutes + $d2_minutes ) . 'M' 
                     . ( string ) ($d1_seconds + $d2_seconds ) . 'S' );

  return $return_dateinterval;
}

另一种方法可能是使用神奇的 __set_state() 方法来创建一个新的 DateInterval,构建一个 属性 值的数组,这些值是根据两个现有的等效属性的总和所需要的间隔。

演示此方法的快速而丑陋的代码块(未经测试):

$properties = ['y' => PHP_INT_MAX,'m' => 12,'d' => 30,'h' => 24,'i' => 60,'s' => 60];


$dti1 = new DateInterval('P1Y7M16DT15H30M15S');
$dti2 = new DateInterval('P2Y8M26DT14H40M55S');

$state = array_fill_keys(array_keys($properties), null);

$carry = 0;
foreach(array_reverse($properties) as $property => $limit) {
    $state[$property] = $dti1->{$property} + $dti2->{$property} + $carry;
    $carry = 0;
    if ($state[$property] > $limit) {
        do {
            $state[$property] -= $limit;
            ++$carry;
        } while ($state[$property] > $limit);
        $state[$property] = $state[$property] % $limit;
    }
}

$newInterval = DateInterval::__set_state($state);

var_dump($newInterval);

它需要一些清理来处理 day 属性,并允许在 PHP 中添加的 f 属性 7.1

2021 年更新

聪明了将近四年,我认为最好的方法是让PHP计算总和并在过程中归一化字段:

function sumDateIntervals(DateInterval $a, DateInterval $b)
{
  $base = new DateTimeImmutable();

  return $base->add($a)->add($b)->diff($base);
}

检查一下online

最初接受的答案

To me, your solution looks like the best way to implement this functionality.

There is no need to explicitly convert the sums to strings (PHP does it anyway). If you want to make it more readable you can use sprintf() to produce the string and shorter names for the arguments:

function sumDateIntervals(DateInterval $a, DateInterval $b)
{
    return new DateInterval(sprintf('P%dY%dM%dDT%dH%dM%dS',
        $a->y + $b->y,
        $a->m + $b->m,
        $a->d + $b->d,
        $a->h + $b->h,
        $a->i + $b->i,
        $a->s + $b->s
    ));
}