首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >DateInterval格式为ISO8601

DateInterval格式为ISO8601
EN

Stack Overflow用户
提问于 2015-11-18 17:58:29
回答 3查看 2.7K关注 0票数 5

我目前正在处理一个php项目,需要将DateInterval格式化为ISO8601 (如下所示):

代码语言:javascript
运行
复制
P5D

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-11-18 18:23:16

那么,如果您在构造格式时查看格式规范:

Y岁 M个月 D天 W周。这些被转换成天,所以不能与D。 H小时 M分钟 秒数

然后看看您必须使用的东西(http://php.net/manual/en/dateinterval.format.php),似乎您要做的事情是:

代码语言:javascript
运行
复制
$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

现在,我做的另一件事是,我总是包括月份,即使它是0。原因是minutesmonths都是由M表示的--如果我们总是包括月份,那么如果有一分钟,我们就知道它是分钟。否则,我们必须做大量的逻辑来查看是否需要将P更改为PT,以便它知道这个实例中的a M代表Minute

例如:

代码语言:javascript
运行
复制
// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("PT3M"));

但相反,我们做到了:

代码语言:javascript
运行
复制
// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("P0M3M"));
票数 2
EN

Stack Overflow用户

发布于 2020-04-07 19:39:51

戴夫提供的解决方案Guss提供的解决方案进行扩展。

要想在ISO 8601标准的持续时间中添加T部分而不需要默认为P0M,一个解决办法是在P0YY替换后插入两个额外的检查。

  • Y0MY取代
  • P0MP取代
  • D0H更改为TD0H,由DT替换
  • 删除被Y0M替换的冗余Y0M

这是因为间隔格式总是会生成相同的结构,而不会导致0和str_replace从左到右遍历数组中的每个替换。因此,我们有可能接收P1YTPT作为返回值。只需使用PT删除任何尾随的rtrim字符即可。然后用所需的默认值替换空值,如Guss的答案中所示。

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

与PHP 5.3+兼容

https://www.php.net/manual/en/language.variables.scope.php#language.variables.scope.static的使用是为了提高对函数重复调用的性能,因为静态变量在离开函数范围后不会丢失它们的值。

代码语言:javascript
运行
复制
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;
}

测试

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

代码语言:javascript
运行
复制
$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));

结果

代码语言:javascript
运行
复制
PT0S

具有微秒级7.1+的https://3v4l.org/VFN7N

通过将S0F替换为第一个数组值的S并将%fF添加到DateInterval::format()中,您可以轻松地添加微秒。

然而,F (微秒)目前并不是DateInterval::__construct()或ISO8601标准所支持的间隔规范。

注意:当差小于1秒时,有一个DateTime::diff阻止正确的DateInterval检索。

代码语言:javascript
运行
复制
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;
}

测试

代码语言:javascript
运行
复制
$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));

结果

代码语言:javascript
运行
复制
PT0F
PT21F
票数 5
EN

Stack Overflow用户

发布于 2017-03-04 16:01:05

关于@dave,我重新实现了他的解决方案,解决了一些问题,特别是始终保留一个月字段的要求。从维基百科关于ISO-8601的文章的角度来看,即使在上面提到的实现中完全缺失了T的名称,它似乎也不是可选的。通过引入它,我们可以解决大部分问题,并编写一些更简洁的代码:

代码语言:javascript
运行
复制
function date_interval_iso_format(DateInterval $interval) {
    list($date,$time) = explode("T",$interval->format("P%yY%mM%dDT%hH%iM%sS"));
    // now, we need to remove anything that is a zero, but make sure to not remove
    // something like 10D or 20D
    $res =
        str_replace([ 'M0D', 'Y0M', 'P0Y' ], [ 'M', 'Y', 'P' ], $date) .
        rtrim(str_replace([ 'M0S', 'H0M', 'T0H'], [ 'M', 'H', 'T' ], "T$time"),"T");
    if ($res == 'P') // edge case - if we remove everything, DateInterval will hate us later
        return 'PT0S';
    return $res;
}

请注意,如果不需要T,则删除它,因此现在M的两种用法都很好:

代码语言:javascript
运行
复制
"P5M" == date_interval_iso_format(new DateInterval("P5M")); // => true
"PT5M" == date_interval_iso_format(new DateInterval("PT5M")); // => true
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33787039

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档