Analyser et créer la date ISO 8601 et les intervalles de temps, comme PT15M en PHP

Une bibliothèque et d'un webservice, je suis en utilisant communique temps-intervalles dans format ISO 8601:PnYnMnDTnHnMnS. Je veux convertir de tels formats en secondes. Et vice-versa. Les secondes sont beaucoup plus facile à calculer.

exemple les valeurs d'intervalle sont:

  • PT1M or PT60S (1 minute)
  • PT1H, PT60M ou PT3600S (1 heure)

j'ai besoin de deux fonctions: analyser de telles valeurs de secondes: iso8601_interval_to_seconds() et de secondes en secondes intervalles: iso8601_interval_from_seconds().

ce dernier est assez simple, car il peut être fait comme "PT{$seconds} s", il suffit de passer des secondes, à tout moment. Peut-être que cela peut être fait plus agréable avec un analyseur qui passe à H(heure) ou M(minute)?

le premier est plus dur, mais peut-être qu'il y a un truc avec L'un des nombreux convertisseurs de date de chaîne en PHP? J'aimerais apprendre comment utiliser cette fonction pour l'analyse d'intervalles. Ou apprendre une alternative.

26
demandé sur Basil Bourque 2010-09-15 23:27:00

5 réponses

on dirait que PHP 5.3 est DateInterval prend en charge cette.

si vous ne pouvez pas utiliser 5.3, je suppose qu'une telle fonction de conversion saurait combien de secondes sont dans une année, un mois, un jour, une heure, et une minute. Puis, lors de la conversion de secondes, il se diviserait dans cet ordre, chaque fois en prenant le modulo de l'opération précédente, jusqu'à ce qu'il ne reste que <60 secondes. Lors de la conversion d'une représentation d'intervalle ISO 8601, il devrait être trivial pour analyser la chaîne et multiplier chaque élément trouvé en conséquence.

17
répondu Fanis 2010-09-15 19:48:41

strtotime ne fonctionnera pas directement avec le format ISO 8601 (par ex. P1Y1DT1S), mais le format qu'il comprend (1 an1day1seconde) n'est pas trop loin -- il serait assez simple de conversion. (un peu "hacky"... mais C'est PHP pour vous).

Merci Lee, Je n'étais pas au courant que strtotime acceptait ce format. C'était la pièce manquante de mon puzzle. Peut-être que mes fonctions peuvent compléter votre réponse.

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;
    }
}

la fonction supporte aussi formats négatifs. - P10Y9MT7M5S retourne un tableau comme: [année] => -10 [mois] => -9 [jour] => 0 [heure] => 0 [minute] => -7 [deuxième] => -5 si ce comportement n'est pas souhaité passer false comme second paramètre. De cette façon, la fonction retournera toujours des valeurs positives. le symbole min/plus sera toujours disponible dans la touche résultat ['symbol'].

et une petite mise à jour: Cette fonction utilise la première fonction permettant d'obtenir le montant total de deuxième.

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
répondu Daan Biesterbos 2012-12-14 21:35:18

sachez que la conversion des durées qui contiennent des jours, des mois et/ou des années en une durée comme des secondes ne peut être faite avec précision sans connaître une date/heure réelle de début.

Par Exemple

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

c'est parce que septembre a 30 jours, et octobre a 31 jours. Le même problème se produit avec la conversion des intervalles D'année, en raison des années bissextiles et des secondes bissextiles. Les années bissextiles introduisent encore plus de complexité à la conversion de mois ainsi-depuis Février est un jour de plus en années bissextiles qu'autrement. Les jours sont problématiques dans les régions où l'heure d'été est utilisée - une période d'une journée qui se produit pendant la transition DST, est en fait plus longue d'une heure qu'elle ne le serait autrement.

tout cela étant dit -- Vous pouvez, bien sûr, compresser des valeurs contenant les Heures, les Minutes et les Secondes dans les valeurs contenant quelques Secondes. Je suggère que vous construisez un simple analyseur de faire le travail (ou peut-être examiner régulièrement expression.)

il suffit d'être conscient des pièges décrits ci-dessus -- il y a des dragons. Si vous avez l'intention de traiter avec les Jours, les Mois et/ou Années, vous devez utiliser l'un des mécanismes intégrés de faire le calcul pour vous, dans le contexte d'une date/heure. Comme d'autres l'ont mentionné: la classe DateInterval, en combinaison avec les fonctions fournies sur la classe DateTime est probablement la manière la plus intuitive de traiter cela. Mais c'est seulement disponible en PHP version 5.3.0 ou plus.

Si vous avez à travailler avec moins de v5.3.0, vous pouvez essayer de construire quelque chose autour de ce petit bijou:

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

strtotime ne fonctionnera pas avec le format ISO 8601 directement (par ex. P1Y1DT1S), mais le format comprendre (1Year1Day1Second) n'est pas trop loin, il serait assez simple de conversion. (un peu "hacky"... mais C'est PHP pour vous).

bonne chance!

7
répondu Lee 2010-09-15 21:18:34
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;
}

Tests:

PT1H         - OK
PT23M        - OK
PT45S        - OK
PT1H23M      - OK
PT1H45S      - OK
PT23M45S     - OK
PT1H23M45S   - OK
1
répondu Mantas D 2017-05-12 07:47:41

ce que vous recherchez est DateTime:: diff

l'objet DateInterval représentant la différence entre les deux dates ou FALSE en cas d'échec tel qu'illustré ci-dessous:

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

Utilisez juste les secondes au lieu des dates.

C'est à partir de http://www.php.net/manual/en/datetime.diff.php

Pour une référence à DateInterval voir http://www.php.net/manual/en/class.dateinterval.php

0
répondu Todd Moses 2010-09-15 19:53:17