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

2022-08-30 14:57:21

我正在使用的库和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中许多字符串到现在转换器之一有一个技巧?我很想学习如何使用这样的函数来解析间隔。或者学习替代方案。


答案 1

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

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


答案 2

strtotime不能直接使用ISO 8601格式(例如。P1Y1DT1S),但它所理解的格式(1Year1Day1Second)并不遥远 - 这将是一个非常直接的转换。(有点“黑客”...但那是你的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 将返回一个数组,如:[年] => -10 [月] => -9 [日] => 0 [小时] => 0 [分钟] => -7 [秒] => -5 如果不需要此行为,则将 false 作为第二个参数传递。这样,函数将始终返回正值。最小值/加号在结果键 ['符号'] 中仍然可用。

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

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

推荐