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

ber*_*kes 27 php date iso8601 intervals

我正在使用的库和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中的许多字符串到今天的转换器之一?我很想学习如何使用这样的函数来解析间隔.或者学习另一种选择.

Fan*_*nis 18

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

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


小智 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

该功能还支持负面格式. -P10Y9MT7M5S将返回如下数组: [year] => -10 [month] => -9 [day] => 0 [小时] => 0 [分钟] => -7 [秒] => -5如果这样不希望行为作为第二个参数传递false.这样,函数将始终返回正值.min/plus符号仍将在结果键['symbol']中可用.

并稍加更新:此函数使用第一个函数来获取总秒数.

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
Run Code Online (Sandbox Code Playgroud)


Lee*_*Lee 7

请注意,如果不知道实际的开始日期/时间,则无法准确地将包含天,月和/或年的持续时间转换为持续时间(如秒).

例如

1 SEP 2010:  +P1M2D means +2764800 seconds
1 OCT 2010:  +P1M2D means +2851200 seconds
Run Code Online (Sandbox Code Playgroud)

那是因为9月有30天,10月有31天.转换年间隔时会出现同样的问题,因为闰年​​和闰秒.闰年也为月转换带来了进一步的复杂性 - 因为2月比闰年更长一天.在实行夏令时的区域中,天数是有问题的 - 在DST过渡期间发生的一天时间实际上比其他时间长1小时.

所有这一切 - 当然,您可以将包含小时,分钟和秒的值压缩为包含秒的值.我建议你构建一个简单的解析器来完成这项工作(或者考虑一个正则表达式).

请注意上面提到的陷阱 - 有龙.如果您打算处理Days,Months和/或Years,则需要使用其中一种内置机制在已知日期/时间的上下文中为您进行数学运算.正如其他人所提到的:DateInterval类与DateTime类提供的函数组合可能是解决此问题的最直观方式.但这仅适用于PHP 5.3.0或更高版本.

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

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

strtotime不会直接与ISO 8601格式的工作(例如P1Y1DT1S),但它的格式理解(1Year1Day1Second)是不是太远了-它会一个非常直接的转换.(有点"hacky"......但那是你的PHP).

祝好运!

  • 起初,它*确实*似乎是直观的答案,但如果你真的想到它,很明显事情真的不能像那样工作.事实证明,"日历时间"(天,月,年)与"绝对时间"(秒,分钟,小时)一样重要 - 当我们使用日历时间时,我们的意思与我们使用绝对时间时的根本不同.例如:你的生日有多远?总是1年,而不是总是8760小时.如果上个月的信用卡账单日期是8月21日,那么这个月的日期是什么时候?9月21日.总是1个月,而不是30天. (5认同)