web-dev-qa-db-ja.com

PHP

私が使用しているライブラリとWebサービスは、時間間隔を ISO 8601形式:PnYnMnDTnHnMnS で通信します。そのようなフォーマットを秒に変換したい。およびその逆。秒は計算がはるかに簡単です。

間隔の値の例は次のとおりです。

  • PT1MまたはPT60S(1分)
  • PT1H、PT60MまたはPT3600S(1時間)

私は2つの関数が必要です。そのような値から秒に解析する場合:iso8601_interval_to_seconds()および秒からそのような間隔に解析する場合:iso8601_interval_from_seconds()

後者は `" PT {$ seconds} S "として実行でき、常に秒を渡すだけなので、かなり単純です。たぶん、これはH(hour)またはM(minute)に切り替わるパーサーでもっとうまくできるでしょうか?

最初は難しいですが、PHPの多くの文字列から日付へのコンバーターの1つにトリックがあるのではないでしょうか?このような関数を使用して区間を解析する方法を学びたいと思います。または代替を学びます。

28
berkes

PHP 5.3の DateInterval がこれをサポートしているようです。

5.3を使用できない場合、そのような変換関数は、年、月、日、時間、分のうち、秒数を知っていると思います。次に、秒から変換すると、その前の演算のモジュロを取るたびに、60秒未満になるまで、その順序で除算されます。 ISO 8601間隔表現から変換する場合、文字列を解析し、見つかった各要素をそれに応じて乗算することは簡単です。

18
Fanis

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は、次のような配列を返します。[year] => -10 [month] => -9 [day] => 0 [時間] => 0 [分] => -7 [秒] => -5この動作が望ましくない場合は、2番目のパラメータとして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
8
user1441149

日、月、年を含む期間を秒などの期間に変換することは、実際の開始日時を知らないと正確に実行できないことに注意してください。

例えば

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

9月は30日、10月は31日だからです。うるう年とうるう秒のため、年の間隔の変換でも同じ問題が発生します。うるう年は、月の変換もさらに複雑にします。2月はうるう年で他の場合よりも1日長いためです。夏時間を採用している地域では、日数に問題があります。DSTの移行中に発生する1日の期間は、実際にはそれ以外の場合より1時間長くなります。

言われていることすべて-もちろん、only時間、分、秒を含む値を、秒のみを含む値に圧縮できます。そのためには、単純なパーサーを作成することをお勧めします(または正規表現を検討することもできます)。

上で概説した落とし穴に注意してください-ドラゴンがいます。日、月、年を扱う場合は、組み込みのメカニズムの1つを使用して、既知の日付/時刻のコンテキストで計算を行う必要があります。他の人が述べたように:DateIntervalクラスは、DateTimeクラスで提供される関数と組み合わせて、おそらくこれに対処する最も直感的な方法です。ただし、これはPHPバージョン5.3.0以降でのみ使用できます。

V5.3.0未満で作業する必要がある場合は、この小さな宝石を中心に何かを構築してみてください。

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

strtotimeは、ISO 8601形式では直接機能しません(例:P1Y1DT1S)ですが、それが行うという形式です(1Year1Day1Second)はそれほど遠くありません-かなり単純な変換になります。 (少し「ハッキー」...しかし、それはPHPあなたにとって)です。).

幸運を!

7
Lee
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
1
Mantas D

あなたが探しているのはDateTime :: diffです

2つの日付の差を表す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 を参照してください

0
Todd Moses