解析并创建 ISO 8601 日期和时间间隔,如 PHP 中的 PT15M

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3721085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 10:51:48  来源:igfitidea点击:

Parse and create ISO 8601 Date and time intervals, like PT15M in PHP

phpdateintervalsiso8601

提问by berkes

A library and webservice I am using communicates time-intervals in ISO 8601 format: PnYnMnDTnHnMnS. I want to convert such formats to seconds. And vice versa. Seconds are a lot easier to calculate with.

我正在使用的库和网络服务以ISO 8601 格式PnYnMnDTnHnMnS通信时间间隔. 我想将这种格式转换为秒。反之亦然。秒更容易计算。

Example interval values are:

示例间隔值是:

  • PT1M or PT60S (1 minute)
  • PT1H, PT60M or PT3600S (1 hour)
  • PT1M 或 PT60S(1 分钟)
  • PT1H、PT60M 或 PT3600S(1 小时)

I need two functions: parse from such values to seconds: iso8601_interval_to_seconds()and from seconds into such intervals: iso8601_interval_from_seconds().

我需要两个函数:从这些值解析到秒:iso8601_interval_to_seconds()和从秒解析到这样的间隔:iso8601_interval_from_seconds()

The latter is rather simple, because it could be done as `"PT{$seconds}S", just pass seconds along, at all times. Maybe this can be done nicer with a parser that switches to H(hour) or M(minute)?

后者相当简单,因为它可以作为 `"PT{$seconds}S" 完成,只需一直传递秒。也许这可以用切换到 H(小时)或 M(分钟)的解析器做得更好?

The first is harder, but maybe there is a trick with one of the many string-to-date converters in PHP? I would love to learn how to use such a function for parsing intervals. Or learn an alternative.

第一个更难,但也许 PHP 中的众多字符串到日期转换器中的一个有一个技巧?我很想学习如何使用这样的函数来解析间隔。或者学习一个替代方案。

采纳答案by Fanis Hatzidakis

It looks like PHP 5.3's DateIntervalsupports this.

看起来 PHP 5.3 的DateInterval支持这一点。

If you can't use 5.3, I suppose any such conversion function would know how many seconds are in a year, a month, a day, an hour, and a minute. Then, when converting from seconds, it would divide in that order, each time taking the modulo of the previous operation, until only <60 seconds are left. When converting from an ISO 8601 interval representation it should be trivial to parse the string and multiply each found element accordingly.

如果你不能使用 5.3,我想任何这样的转换函数都会知道一年、一个月、一天、一个小时和一分钟有多少秒。然后,当从秒转换时,它会按这个顺序划分,每次取前一个操作的模,直到只剩下 <60 秒。从 ISO 8601 区间表示转换时,解析字符串并相应地乘以每个找到的元素应该是微不足道的。

回答by Fanis Hatzidakis

strtotime won't work with the ISO 8601 format directly (eg. P1Y1DT1S), but the format that it does understand (1Year1Day1Second) is not too far off -- it would a pretty straight-forward conversion. (a little "hacky"... but that's PHP for you).

strtotime 不能直接使用 ISO 8601 格式(例如 P1Y1DT1S),但它确实理解的格式(1Year1Day1Second)并不太远——这将是一个非常直接的转换。(有点“hacky”......但那是你的PHP)。

Thanks Lee, I was not aware strtotime accepted this format. This was the missing part of my puzzle. Perhaps my functions can complete your answer.

谢谢李,我不知道 strtotime 接受这种格式。这是我的谜题中缺失的部分。或许我的功能可以完成你的回答。

function parse_duration($iso_duration, $allow_negative = true){
    // Parse duration parts
    $matches = array();
    preg_match('/^(-|)?P([0-9]+Y|)?([0-9]+M|)?([0-9]+D|)?T?([0-9]+H|)?([0-9]+M|)?([0-9]+S|)?$/', $iso_duration, $matches);
    if(!empty($matches)){       
        // Strip all but digits and -
        foreach($matches as &$match){
            $match = preg_replace('/((?!([0-9]|-)).)*/', '', $match);
        }   
        // Fetch min/plus symbol
        $result['symbol'] = ($matches[1] == '-') ? $matches[1] : '+'; // May be needed for actions outside this function.
        // Fetch duration parts
        $m = ($allow_negative) ? $matches[1] : '';
        $result['year']   = intval($m.$matches[2]);
        $result['month']  = intval($m.$matches[3]);
        $result['day']    = intval($m.$matches[4]);
        $result['hour']   = intval($m.$matches[5]);
        $result['minute'] = intval($m.$matches[6]);
        $result['second'] = intval($m.$matches[7]);     
        return $result; 
    }
    else{
        return false;
    }
}

The function also supports negative formats. -P10Y9MT7M5Swill return an array like: [year] => -10 [month] => -9 [day] => 0 [hour] => 0 [minute] => -7 [second] => -5If this behaviour is not desired pass false as second parameter. This way the function will always return positive values. The min/plus symbol will still be available in result key ['symbol'].

该函数还支持负片格式。 -P10Y9MT7M5S将返回一个数组,如: [year] => -10 [month] => -9 [day] => 0 [hour] => 0 [minute] => -7 [second] => -5If this不希望行为传递 false 作为第二个参数。这样,函数将始终返回正值。最小/加号符号仍将在结果键 ['symbol'] 中可用。

And a little update: This function uses the first function to get the total amount of seconds.

还有一点更新:这个函数使用第一个函数来获取总秒数。

function get_duration_seconds($iso_duration){
    // Get duration parts
    $duration = parse_duration($iso_duration, false);
    if($duration){
        extract($duration);
        $dparam  = $symbol; // plus/min symbol
        $dparam .= (!empty($year)) ? $year . 'Year' : '';
        $dparam .= (!empty($month)) ? $month . 'Month' : '';
        $dparam .= (!empty($day)) ? $day . 'Day' : '';
        $dparam .= (!empty($hour)) ? $hour . 'Hour' : '';
        $dparam .= (!empty($minute)) ? $minute . 'Minute' : '';
        $dparam .= (!empty($second)) ? $second . 'Second' : '';
        $date = '19700101UTC';
        return strtotime($date.$dparam) - strtotime($date);
    }
    else{
        // Not a valid iso duration
        return false;
    }
}

$foo = '-P1DT1S';
echo get_duration_seconds($foo); // Output -86399
$bar = 'P1DT1S';
echo get_duration_seconds($bar); // Output 86401

回答by Lee

Be aware that converting durations that contain Days, Months, and/or Years into a duration like seconds can not be done accurately without knowing an actual starting date/time.

请注意,在不知道实际开始日期/时间的情况下,无法准确地将包含天、月和/或年的持续时间转换为像秒这样的持续时间。

For Example

例如

1 SEP 2010:  +P1M2D means +2764800 seconds
1 OCT 2010:  +P1M2D means +2851200 seconds

That's because September has 30-days, and October has 31-days. The same problem occurs with converting Year intervals, because of leap-years and leap-seconds. Leap-years introduce further complexity to the Month conversion as well - since February is one day longer in leap-years than it is otherwise. Days are problematic in areas where daylight saving time is practiced - a one-day period occurring during the DST transition, is actually 1-hour longer than it would be otherwise.

那是因为九月有 30 天,十月有 31 天。由于闰年和闰秒,转换年份间隔也会出现同样的问题。闰年也给月份转换带来了进一步的复杂性——因为闰年的二月比其他月份长一天。在实行夏令时的地区,天数是有问题的——在 DST 过渡期间发生的一天时间实际上比其他情况长 1 小时。

All that being said -- you can, of course, compress values containing onlyHours, Minutes, and Seconds into values containing just Seconds. I'd suggest that you build a simple parser to do the job (or maybe consider a regular expression).

说了这么多——当然,您可以将包含小时、分钟和秒的值压缩为包含秒的值。我建议您构建一个简单的解析器来完成这项工作(或者考虑使用正则表达式)。

Just be aware of the pitfalls outlined above -- there there be dragons. If you intend to deal with Days, Months, and/or Years, you need to use one of the built-in mechanisms to do the math for you in the context of a known date/time. As others have mentioned: the DateInterval class, in combination withe the functions provided on the DateTime class is probably the most intuitive way to deal with this. But that's only available in PHP version 5.3.0 or greater.

请注意上面概述的陷阱 - 有龙。如果您打算处理天、月和/或年,则需要使用其中一种内置机制在已知日期/时间的上下文中为您进行数学计算。正如其他人所提到的:DateInterval 类与 DateTime 类上提供的函数相结合可能是处理这个问题的最直观的方法。但这仅在 PHP 5.3.0 或更高版本中可用。

If you have to work with less than v5.3.0, you can try to build something around this little gem:

如果您必须使用低于 v5.3.0 的版本,您可以尝试围绕这个小宝石构建一些东西:

$startDateTime = '19700101UTC';
$duration = strtotime( $startDateTime.'+1Year1Day1Second' ) - strtotime($startDateTime);
print("Duration in Seconds:  $duration");

strtotimewon't work with the ISO 8601 format directly (eg. P1Y1DT1S), but the format that it doesunderstand (1Year1Day1Second) is not too far off -- it would a pretty straight-forward conversion. (a little "hacky"... but that's PHP for you).

strtotime不会直接使用 ISO 8601 格式(例如P1Y1DT1S),但它确实理解的格式( 1Year1Day1Second) 并不太远——这将是一个非常直接的转换。(有点“hacky”......但那是你的PHP)。

good luck!

祝你好运!

回答by Mantas D

function parsePTTime($pt_time)
{
    $string = str_replace('PT', '', $pt_time);
    $string = str_replace('H', 'Hour', $string);
    $string = str_replace('M', 'Minute', $string);
    $string = str_replace('S', 'Second', $string);

    $startDateTime = '19700101UTC';
    $seconds = strtotime($startDateTime . '+' . $string) - strtotime($startDateTime);

    return $seconds;
}

Tests:

测试:

PT1H         - OK
PT23M        - OK
PT45S        - OK
PT1H23M      - OK
PT1H45S      - OK
PT23M45S     - OK
PT1H23M45S   - OK

回答by Todd Moses

What you are looking for is DateTime::diff

您正在寻找的是 DateTime::diff

The DateInterval object representing the difference between the two dates or FALSE on failure as illustrated below:

DateInterval 对象表示两个日期之间的差异或失败时 FALSE,如下图所示:

$datetime1 = new DateTime('2009-10-11');
$datetime2 = new DateTime('2009-10-13');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%R%d days');

Just use seconds instead of dates.

只需使用秒而不是日期。

This is from http://www.php.net/manual/en/datetime.diff.php

这是来自http://www.php.net/manual/en/datetime.diff.php

For a reference to DateInterval see http://www.php.net/manual/en/class.dateinterval.php

有关 DateInterval 的参考,请参阅http://www.php.net/manual/en/class.dateinterval.php