28

我正在使用的库和 Web 服务以ISO 8601 格式传达时间间隔:PnYnMnDTnHnMnS . 我想将这些格式转换为秒。反之亦然。秒数更容易计算。

示例间隔值是:

  • PT1M 或 PT60S(1 分钟)
  • PT1H、PT60M 或 PT3600S(1 小时)

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

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

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

4

5 回答 5

18

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

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

于 2010-09-15T19:42:44.580 回答
8

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

谢谢李,我不知道 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;
    }
}

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

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

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
于 2012-12-14T20:25:41.930 回答
7

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

例如

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

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

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

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

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

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

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

祝你好运!

于 2010-09-15T21:12:21.803 回答
1
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;
}

测试:

PT1H         - OK
PT23M        - OK
PT45S        - OK
PT1H23M      - OK
PT1H45S      - OK
PT23M45S     - OK
PT1H23M45S   - OK
于 2017-05-12T07:47:41.833 回答
0

您正在寻找的是 DateTime::diff

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

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

只需使用秒而不是日期。

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

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

于 2010-09-15T19:53:17.067 回答