Zeitintervalle (Erledigungsfristen) in PHP

timeIch bin wiedereinmal am Coden und irgendwie scheine ich mit fortschreitendem Alter auf immer mehr ungelöste Probleme zu treffen. In der aktuellen Applikation geht es darum, dass Erledigungsfristen als Intervalle eingegeben werden können: Beispielsweise muss eine Aufgabe immer in 2 Monaten, 1 Woche und 4 Sekunden nach dem Erfassen erledigt sein. Daraus ergeben sich ein Strauss an Problemen (oder ein Problemkorb, wie man neuerdings zu sagen scheint. Was für eine schreckliche Vorstellung, nur FRÜCHTE-Körbe finde ich noch abschreckender):

  • Jemand muss die Aufgabe erledigen. Zum Glück ist das ein PAL (Problem anderer Leute) 🙂 .
  • „In einem Monat“ und „In einem Jahr“ kann nicht trivial in Sekunden umgerechnet werden. Irgendein Soziopath hat mal bestimmt, dass nicht alle Monate und Jahre die gleiche Anzahl Tage haben. Ganz zu schweigen von Schaltsekunden und Sommer-/Winterzeit.
  • Aus hier zu verschweigenden Gründen habe ich weder Lust noch Zeit ein GUI mit 7 Textfeldern für Jahre, Monate, Wochen, Tage, Stunden, Minuten und Sekunden aufzubauen.

Ich habe mich für ein simples Eingabefeld entschieden, in denen das Erledigungsintervall in der Form „Zahl Zeitbezeichner Zahl Zeitbezeichner …“ eingegeben werden kann. In obigem Beispiel wäre das: „2m 1w 4s„.

Leider ist die Aufgabe ein Bisschen zu klein um einen richtigen Parser hochzufahren 🙁 , und so habe ich die kleineren Kanonen hervorgeholt. Meine Idee is die Folgende: Dieses Intervall wird in einen String übersetzt, der direkt an strtotime übergeben werden kann. Soll sich diese Funktion doch um die Hässlichkeiten der Zeit kümmern.

Mein Ansatz ist zweistufig: Erst wird geparst und ein Array aus den Tokens erzeugt. Im Idealfall also immer abwechselnd „number“ und „alpha“. Im zweiten Schritt wird dann der String daraus geformt. Hier der Code für alle armen Seelen, die vielleicht mal auf dasselbe exotische Problem treffen sollten. Mir ist bewusst, dass ich auch mit RegExpen hätte arbeiten können, aber die sind eh nur für Weicheier :):

/**
 * Parses a string with an interval into a string that is understood by strtotime.
 *
 * Example: "3w 2d 4h 2s" means 3 Weeks, 2 days, 4 hours
 */

function timeIntervalToStrtotime($timeInterval) {
    $tokens=array();
    $currentTokenType="";
    $currentToken="";

    // Poor man's scanner
    for($i=0; $i<strlen($timeInterval); $i++) {

        // Ignore whitespaces
        if(ctype_space($timeInterval[$i])) {
            continue;
        }

        // Numbers or names
        $thisTokenType=ctype_digit($timeInterval[$i])?"numeric":"alpha";

        if($currentTokenType == $thisTokenType) {
            $currentToken.=$timeInterval[$i];
        } // Else
        if($currentTokenType != $thisTokenType) { // New Tokentype
            if($currentTokenType!="") { // Not first token
                // Save old token
                $tokens[]= array("type" => $currentTokenType, "token" => $currentToken);
            } else { // First token must be numeric
                if($thisTokenType!="numeric") {
                    return "";
                }
            }
            $currentTokenType=$thisTokenType;
            $currentToken=$timeInterval[$i];
        }
    }
    // Save last Token
    if($currentTokenType!="") {
        $tokens[]= array("type" => $currentTokenType, "token" => $currentToken);
    }

    // Poor man's parser

    // There has to be an even number of tokens
    if(count($tokens) % 2 != 0) {
        return "";
    }

    $ret=array();
    for($i=0; $i<count($tokens); $i+=2) {
        switch(strtolower($tokens[$i+1]["token"])) {
            case "a":
            case "y":
            case "yr":
            case "year":
            case "years": $ret[]="+".$tokens[$i]["token"]." years"; break;
            case "m":
            case "mon":
            case "month":
            case "months": $ret[]="+".$tokens[$i]["token"]." months"; break;
            case "w":
            case "week":
            case "weeks": $ret[]="+".$tokens[$i]["token"]." weeks"; break;
            case "d":
            case "day":
            case "days": $ret[]="+".$tokens[$i]["token"]." days"; break;
            case "h":
            case "hr":
            case "hour":
            case "hours": $ret[]="+".$tokens[$i]["token"]." hours"; break;
            case "m":
            case "min":
            case "mins":
            case "minute":
            case "minutes": $ret[]="+".$tokens[$i]["token"]." minutes"; break;
            case "s":
            case "sec":
            case "secs":
            case "second":
            case "seconds": $ret[]="+".$tokens[$i]["token"]." seconds"; break;
            default: return "";
        }
    }
    return join(" ", $ret);
}

$tests=array("1s", "1", " 1y 2d 3 mins 4sec", " 15 yr ", "a2 3mon");
foreach($tests as $test) {
    echo "TEST:[$test]: ".timeIntervalToStrtotime($test)."\n";
}

2 Gedanken zu “Zeitintervalle (Erledigungsfristen) in PHP

  1. Naja… das kann ich auch _direkt_ in strtotime eingeben… verstehe also den eigentlichen Sinn davon nicht so ganz

  2. Genau das habe ich gesucht. Stehe mit meiner Turn Community (gymbase) vor dem gleichen Problem (dort hat jeder seinen eigenen Kalender). Sehr guter Lösungsansatz. Werde ich so übernehmen. Danke!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.