在 PHP 中标准化 DateInterval

Normalize DateInterval in PHP

situation/issue

我对 PHP 中的 DateInterval 有疑问。

给定以下代码

$interval = new DateInterval("PT6000S");
echo $interval->h; // 0
echo $interval->i; // 0
echo $interval->s; // 6000
echo $interval->format("%h:%i"); // 0:0

我希望它代表 1 小时 40 分钟,而不是 6000 秒。

问题

有没有内置的方法来标准化 DateInterval? duration 字符串的具体写法?或者这是必须正常完成的事情?

如果有人有“解决方法”建议,我可以修改它的创建和格式化方式。

我已经做了研究,但奇怪的是我没有发现任何对这个问题有帮助的东西。

解决方案

就像每次寻求帮助时一样,我总是不停地尝试,而且通常都能立即找到答案。这次也不例外。

我找到了 user contributed note in the DateInterval documentation

您必须创建两个 DateTime(它们可以是任何日期和时间),将间隔添加到第二个并从第二个中减去第一个。这将以规范化的方式填充 DateInterval。

这是用户编写的代码,封装在 handy-dandy 函数中:

public function createDateInterval(string $duration): DateInterval
{
    $d1 = new DateTime();
    $d2 = new DateTime();
    $d2->add(new DateInterval($duration));
    return $d2->diff($d1);
}

(请注意,如果您的 $duration 字符串格式不正确,此函数可能会引发异常。)

DateInterval class 这样有点烦人。严格来说,它做的是正确的,因为你的间隔规范字符串指定了零分钟和零小时,但它不会将多余的秒数滚动到分钟和小时,这绝对没有帮助。

我之前使用的一个选项是将间隔添加到初始化为 Unix 纪元的新 DateTime 对象,然后改为格式化结果时间。这意味着使用标准日期格式字符串(因此 h 而不是 %h,等等)

echo (new DateTime('@0'))->add(new DateInterval("PT6000S"))->format('h:i');
// 01:40

https://3v4l.org/OX6JF

此解决方案使用具有固定时间的 DateTime。为什么?

DateTime 也支持微秒。因此,有一定的概率 2 次调用 new Date() 不会同时 return。那么我们可能会得到错误的结果。

//360000 Sec = 100 hours = 4 Days + 4 hours
$di = new DateInterval("PT360000S");

$diNormalize = date_create('@0')->diff(date_create('@0')->add($di));

var_export($diNormalize);
/*
DateInterval::__set_state(array(
   'y' => 0,
   'm' => 0,
   'd' => 4,
   'h' => 4,
   'i' => 0,
   's' => 0,
   'f' => 0.0,
   'weekday' => 0,
   'weekday_behavior' => 0,
   'first_last_day_of' => 0,
   'invert' => 0,
   'days' => 4,
   'special_type' => 0,
   'special_amount' => 0,
   'have_weekday_relative' => 0,
   'have_special_relative' => 0,
)) 
*/

echo $diNormalize->format('%a days %h hours %i minutes');
//4 days 4 hours 0 minutes 

DateInterval 有一个特殊的 format 输出方法。这应该在这里使用,并且不应绕过 date、gmdate 或 DateTime。