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

前端 未结 5 481
無奈伤痛
無奈伤痛 2020-12-29 21:23

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

5条回答
  •  星月不相逢
    2020-12-29 21:38

    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).

    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.

    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. -P10Y9MT7M5S will return an array like: [year] => -10 [month] => -9 [day] => 0 [hour] => 0 [minute] => -7 [second] => -5 If 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'].

    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
    

提交回复
热议问题