2010-09-15 9 views
22

Eine Bibliothek und Webservice, die ich verwende, kommuniziert Zeitintervalle in ISO 8601: PnYnMnDTnHnMnS. Ich möchte solche Formate in Sekunden umwandeln. Und umgekehrt. Sekunden sind viel einfacher zu berechnen mit.Parse und erstellen ISO 8601 Datum und Zeitintervalle, wie PT15M in PHP

Beispiel Intervallwerte sind:

  • PT1M oder PT60S (1 Minute)
  • PT1H, PT60M oder PT3600S (1 Stunde)

Ich brauche zwei Funktionen: Parsen von solchen Werten zu Sekunden: iso8601_interval_to_seconds() und von Sekunden in solche Intervalle: iso8601_interval_from_seconds().

Letzteres ist ziemlich einfach, weil es als "PT {$ Sekunden} S" durchgeführt werden könnte, nur Sekunden dauern, zu jeder Zeit. Vielleicht kann dies mit einem Parser, der auf H (Stunde) oder M (Minute) umschaltet, schöner gemacht werden?

Die erste ist schwieriger, aber vielleicht gibt es einen Trick mit einem der vielen String-to-Date-Konverter in PHP? Ich würde gerne lernen, wie man eine solche Funktion zum Parsen von Intervallen benutzt. Oder lerne eine Alternative.

+0

Eine sehr spät Vorsicht ... Umwandlung P1MT auf Sekunden unmöglich sein würde, es sei denn, Sie wissen, ob Sie einen Monat mit 28 konvertieren, 29, 30, oder 31 Tage. In diesem Fall können Sie P1YT nicht einmal in 365 Tage umwandeln, ohne jedes vierte Jahr falsch zu liegen. (Es gibt einen Grund, warum im Standard keine Intervalle in Tagen oder Sekunden angegeben sind.) – Bevan

+0

@Bevan: Die Antwort von Lee geht damit ins Detail. TL; DR: Sie brauchen ein Startdatum. – berkes

+0

Ahh - danke @berkes, habe ich vermisst, als wenn ich die Seite überflogen habe (ich habe es gefunden, während ich nach etwas gesucht habe, das nichts miteinander zu tun hat). – Bevan

Antwort

17

Es sieht aus wie PHP 5.3 DateInterval unterstützt dies.

Wenn Sie 5.3 nicht verwenden können, nehme ich an, dass eine solche Umwandlungsfunktion weiß, wie viele Sekunden in einem Jahr, einem Monat, einem Tag, einer Stunde und einer Minute liegen. Dann, wenn von Sekunden umgewandelt wird, würde es sich in dieser Reihenfolge teilen, wobei jedes Mal der Modulo der vorherigen Operation genommen wird, bis nur noch < 60 Sekunden übrig sind. Bei der Konvertierung von einer ISO 8601-Intervalldarstellung sollte es trivial sein, die Zeichenfolge zu analysieren und jedes gefundene Element entsprechend zu multiplizieren.

+1

DateInterval ('PT1H') funktioniert wie ein Charme!Nun möchte ich vielleicht einen besseren Weg finden, um diese Strings, anders als PTXXXS, in Sekundenschnelle neu zu erstellen :) – berkes

+0

⚠️ PHP '' DateInterval() 'hat einen Fehler, der keine Brüche in den analysierten ISO 8601-Darstellungen zulässt : https://bugs.php.net/bug.php?id=53831 –

0

Was Sie suchen ist Datetime :: diff

Das DateInterval Objekt die Differenz zwischen den beiden Daten oder FALSE bei einem Fehler darstellt, wie unten dargestellt:

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

Nur Sekunden anstelle von Terminen. Diese

ist von http://www.php.net/manual/en/datetime.diff.php

Für einen Verweis auf DateInterval http://www.php.net/manual/en/class.dateinterval.php siehe

+0

Ich sehe nicht, wie dies hilft, die PT10M-ähnliche ISO 8601-Formate hin und her zu konvertieren. – berkes

7

Beachten Sie, dass Dauern Umwandlung von Tagen, Monaten enthalten, und/oder Jahre in einer Dauer wie Sekunden nicht genau durchgeführt werden, ohne zu wissen ein tatsächlicher Starttermin.

Für Beispiel

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

Das liegt daran, 30. September-Tage hat, und Oktober hat 31-Tage. Dasselbe Problem tritt bei der Konvertierung von Jahresintervallen aufgrund von Schaltjahren und Schaltsekunden auf. Leap-Years führen zu einer weiteren Komplexität der Monatsumrechnung, da der Februar in Schaltjahren einen Tag länger ist als sonst. Tage sind problematisch in Gebieten, in denen Sommerzeit praktiziert wird - eine Ein-Tages-Periode, die während des DST-Übergangs auftritt, ist tatsächlich 1 Stunde länger als es sonst sein würde.

Alles was gesagt wird - Sie können natürlich Werte, die nur Stunden, Minuten und Sekunden enthalten, in Werte komprimieren, die nur Sekunden enthalten. Ich würde vorschlagen, dass Sie einen einfachen Parser erstellen, um die Aufgabe zu erledigen (oder vielleicht einen regulären Ausdruck in Betracht ziehen).

Seien Sie sich der oben beschriebenen Gefahren bewusst - da sind Drachen. Wenn Sie mit Tagen, Monaten und/oder Jahren zu tun haben, müssen Sie einen der eingebauten Mechanismen verwenden, um die Mathematik für Sie im Kontext eines bekannten Datums/Zeitpunkts zu berechnen. Wie andere bereits erwähnt haben: Die DateInterval-Klasse ist in Kombination mit den Funktionen der DateTime-Klasse wahrscheinlich der intuitivste Weg, um damit umzugehen. Aber das ist nur in PHP Version 5.3.0 oder höher verfügbar.

Wenn Sie als v5.3.0 mit weniger arbeiten müssen, können Sie versuchen, etwas, um dieses kleine Juwel zu bauen:

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

strtotime nicht direkt mit dem ISO 8601-Format arbeiten (zB P1Y1DT1S.) , aber das Format, dass es tut verstehen (1Year1Day1Second) ist nicht zu weit weg - es wäre eine ziemlich geradlinige Umwandlung. (ein bisschen "hacky" ... aber das ist PHP für dich).

viel Glück!

+0

Wenn ich intuitiv darüber nachdenke, würde ich erwarten, dass das Umschreiben einer absoluten Anzahl von Sekunden wie Jahre + Monate + Tage + Stunden + Minuten + Sekunden den tatsächlichen Wert nicht ändert, dh wo auch immer ein variables Intervall ist, der Standard Wert wird gewählt. Also, für Monate wären das 30 Tage = 1 Kalendermonat. Ich versuche zu finden, wo genau sie dies im Standard selbst angeben, aber es wird spät, und ich kann nur den endgültigen Entwurf sehen, ohne zu bezahlen: http://xml.coverpages.org/ISO-FDIS-8601.pdf – Fanis

+5

At erstens scheint es, dass das die intuitive Antwort wäre, aber wenn man wirklich darüber nachdenkt, wird es offensichtlich, dass die Dinge wirklich nicht so funktionieren können. Stellt sich heraus, dass "Kalenderzeit" (Tage, Monate, Jahre) genauso wichtig ist wie "absolute Zeit" (Sekunden, Minuten, Stunden) - wenn wir Kalenderzeit verwenden, meinen wir etwas grundlegend anderes als wenn wir absolute Zeit verwenden. Zum Beispiel: Wie weit sind Ihre Geburtstage auseinander? Immer 1 Jahr, nicht immer 8760 Stunden. Wenn die Kreditkartenabrechnung des letzten Monats vom 21. August datiert wurde, wie lautet das Datum dieses Monats? 21. September. Immer 1 Monat, nicht 30 Tage. – Lee

8

strtotime wird mit dem ISO 8601-Format direkt nicht arbeiten (zB P1Y1DT1S.), aber das Format, das es verstanden hat (1Year1Day1Second) ist auch nicht weit weg - es wäre eine ziemlich geradlinig Umwandlung . (ein wenig "hacky" ... aber das ist PHP für Sie).

Dank Lee, mir war nicht bewusst, dass strtotime dieses Format akzeptiert. Das war der fehlende Teil meines Puzzles. Vielleicht können meine Funktionen Ihre Antwort vervollständigen.

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

Die Funktion unterstützt auch negative Formate. -P10Y9MT7M5S wird ein Array zurück wie: [Jahr] => -10 [Monat] => -9 [Tag] => 0 [h] => 0 [Minute] => -7 [Sekunde] = > -5 Wenn dieses Verhalten nicht gewünscht ist, übergeben Sie false als zweiten Parameter. Auf diese Weise gibt die Funktion immer positive Werte zurück. Das Min/Plus-Symbol ist weiterhin in der Ergebnistaste ['Symbol'] verfügbar.

Und ein kleines Update: Diese Funktion verwendet die erste Funktion, um die Gesamtanzahl der Sekunden zu erhalten.

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 
+0

Hallo, gibt es jetzt eine einfachere Methode mit PHP 5.4? Nicht dass ich dir für diese Funktion nicht sehr danken möchte, aber vielleicht gibt es schon eine sauberere Methode. – andreszs

+0

Ich schrieb dies vor Ewigkeiten heheh. Ich denke, ich könnte die Funktion umschreiben. Ich habe jetzt wenig Zeit, aber ich werde später nachsehen. –

+0

Können Sie die Pipe '|' Zeichen erklären? Auf sie folgt kein alternativer Zweig, aber ihnen ist kein Fragezeichen '? |' Vorangestellt, um auch Rückreferenzen miteinander zu verbinden. http://php.net/manual/de/regexp.reference.subpatterns.php Eine leere Alternative wird nicht benötigt, da der '?' Quantifizierer verwendet wird. – CoDEmanX

1
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