在 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
此解决方案使用具有固定时间的 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。
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
此解决方案使用具有固定时间的 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。