格式 DateInterval 为 ISO8601

Zwi*_*art 5 php datetime iso8601 dateinterval

我目前正在处理一个 php 项目,需要将 DateInterval 格式化为 ISO8601(类似这样):

P5D
Run Code Online (Sandbox Code Playgroud)

这种格式可用于创建 DateTime 和 DateInterval 对象,但我想不出将 DateInterval 格式化为这种格式的方法。有没有?如果不是,那么轻量级的解决方案可能是什么?

fyr*_*rye 5

扩展Dave提供解决方案Guss 提供解决方案

添加TISO 8601 标准的持续时间部分而不需要默认为 的解决方法P0M是在P0Y替换为之后插入 2 个额外的检查Y

  • Y0M 取而代之 Y
  • P0M 取而代之 P
  • 更改D0HTD0H替换为DT
  • 删除多余的Y0M替换为Y0M

这是有效的,因为间隔格式将始终生成相同的结构,没有前导 0 并str_replace从左到右遍历数组中的每个替换。因此,我们有可能接收P1YTPT作为返回值。只需PT使用rtrim. 然后用所需的默认值替换一个空值,如 Guss 的回答。

ISO 8601 断言:https : //3v4l.org/c55kL

兼容 PHP 5.3+

static 关键字的使用是为了提高对函数重复调用的性能,因为静态变量在离开函数作用域后不会丢失它们的值

function date_interval_iso(DateInterval $interval, $default = 'PT0S') {
    static $f = array('M0S', 'H0M', 'DT0H', 'M0D', 'P0Y', 'Y0M', 'P0M');
    static $r = array('M', 'H', 'DT', 'M', 'P', 'Y', 'P');

    return rtrim(str_replace($f, $r, $interval->format('P%yY%mM%dDT%hH%iM%sS')), 'PT') ?: $default;
}
Run Code Online (Sandbox Code Playgroud)

测试

为了进行比较,我创建了一个数组,其中包含每个可能的持续时间组合(不包括微秒),值为 1。可以在上面的示例链接中查看。

$durations ['P1Y', /*...*/ 'P1Y1M1DT1H1M1S'];
$isos = array();
foreach ($durations as $duration) {
    $isos[] = date_interval_iso(new DateInterval($duration));
}

$diff = array_diff($durations, $isos);
if (!empty($diff)) {
    //output any differences
    var_dump($diff);
}

//test 0 duration DateInterval 
$date1 = new DateTime();
echo date_interval_iso($date1->diff($date1));
Run Code Online (Sandbox Code Playgroud)

结果

PT0S
Run Code Online (Sandbox Code Playgroud)

PHP 7.1+ 微秒https://3v4l.org/VFN7N

您可以轻松添加微秒,方法是包含S0F替换S为第一个数组值并添加%fF到您的DateInterval::format().

但是F(微秒)目前不是DateInterval::__construct()ISO 8601 标准支持的间隔规范。

注意:PHP <= 7.2.13 中DateTime::diff存在一个错误,DateInterval当差异小于一秒时会阻止正确检索。

function date_interval_iso(DateInterval $interval, string $default = 'PT0F') {
    static $f = ['S0F', 'M0S', 'H0M', 'DT0H', 'M0D', 'P0Y', 'Y0M', 'P0M'];
    static $r = ['S', 'M', 'H', 'DT', 'M', 'P', 'Y', 'P'];

    return rtrim(str_replace($f, $r, $interval->format('P%yY%mM%dDT%hH%iM%sS%fF')), 'PT') ?: $default;
}
Run Code Online (Sandbox Code Playgroud)

测试

$date1 = new DateTimeImmutable();
$date2 = new DateTimeImmutable();

//test 0 duration DateInterval 
echo date_interval_iso($date1->diff($date1));

//test microseconds
echo date_interval_iso($date2->diff($date1));
Run Code Online (Sandbox Code Playgroud)

结果

PT0F
PT21F
Run Code Online (Sandbox Code Playgroud)


dav*_*ave 2

好吧,如果您在构建格式时查看格式规范:

Y 年

米月

D天

W周。这些会转换为天数,因此不能与 D 组合。

小时

米分钟

S秒

然后看看你必须使用什么(http://php.net/manual/en/dateinterval.format.php),看起来你要做的是:

$dateInterval = new DateInterval( /* whatever */ );
$format = $dateInterval->format("P%yY%mM%dD%hH%iM%sS");
//P0Y0M5D0H0M0S
//now, we need to remove anything that is a zero, but make sure to not remove
//something like 10D or 20D
$format = str_replace(["M0S", "H0M", "D0H", "M0D", "Y0M", "P0Y"], ["M", "H", "D", "M", "Y0M", "P"], $format);
echo $format;
//P0M5D
Run Code Online (Sandbox Code Playgroud)

现在,我做的不同的一件事是我总是包括月份,即使它是 0。这样做的原因是minutesmonths都表示为M- 如果我们总是包括月份,那么如果有一分钟我们知道它是分钟。否则,我们必须执行一堆逻辑来查看是否需要更改 to,P以便PT它知道此实例中的 aM代表Minute

例如:

// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("PT3M"));
Run Code Online (Sandbox Code Playgroud)

但我们却这样做:

// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("P0M3M"));
Run Code Online (Sandbox Code Playgroud)

  • 有点遗憾 PHP 不提供这个功能。另一个修复错误或缺失功能的功能,耶! (6认同)