How to convert string duration to ISO 8601 duration format? (e.g. "30 minutes" to "PT30M")

I'd convert it to a number first, then work with that.

First, use strtotime():

$time = strtotime("1 hour 30 minutes", 0);

Then you can parse it for duration, and output in PnYnMnDTnHnMnS format. I'd use the following method (inspired by http://csl.sublevel3.org/php-secs-to-human-text/):

function time_to_iso8601_duration($time) {
    $units = array(
        "Y" => 365*24*3600,
        "D" =>     24*3600,
        "H" =>        3600,
        "M" =>          60,
        "S" =>           1,
    );

    $str = "P";
    $istime = false;

    foreach ($units as $unitName => &$unit) {
        $quot  = intval($time / $unit);
        $time -= $quot * $unit;
        $unit  = $quot;
        if ($unit > 0) {
            if (!$istime && in_array($unitName, array("H", "M", "S"))) { // There may be a better way to do this
                $str .= "T";
                $istime = true;
            }
            $str .= strval($unit) . $unitName;
        }
    }

    return $str;
}

The result: http://codepad.org/1fHNlB6e


Here is a simplified version of Eric's time_to_iso8601_duration() function. It doesn't loose precision (365 days approximation of a year) and is about 5x faster. The output is less pretty but still ISO 8601 compatible according to this page.

function iso8601_duration($seconds)
{
    $days = floor($seconds / 86400);
    $seconds = $seconds % 86400;

    $hours = floor($seconds / 3600);
    $seconds = $seconds % 3600;

    $minutes = floor($seconds / 60);
    $seconds = $seconds % 60;

    return sprintf('P%dDT%dH%dM%dS', $days, $hours, $minutes, $seconds);
}

Another approach is to write function based on the DateInterval object. The ISO 8601 duration format is well described.

The advantage of this approach is that it only outputs the relevant (present) representations and it supports empty durations (commonly denoted as 'PT0S' or 'P0D').

function dateIntervalToISO860Duration(\DateInterval $d) {
    $duration = 'P';
    if (!empty($d->y)) {
        $duration .= "{$d->y}Y";
    }
    if (!empty($d->m)) {
        $duration .= "{$d->m}M";
    }
    if (!empty($d->d)) {
        $duration .= "{$d->d}D";
    }
    if (!empty($d->h) || !empty($d->i) || !empty($d->s)) {
        $duration .= 'T';
        if (!empty($d->h)) {
            $duration .= "{$d->h}H";
        }
        if (!empty($d->i)) {
            $duration .= "{$d->i}M";
        }
        if (!empty($d->s)) {
            $duration .= "{$d->s}S";
        }
    }
    if ($duration === 'P') {
        $duration = 'PT0S'; // Empty duration (zero seconds)
    }
    return $duration;
}

An example:

echo dateIntervalToISO860Duration(
    date_diff(
        new DateTime('2017-01-25 18:30:22'), 
        new DateTime('2019-03-11 07:12:17')
    )
);

Output: P2Y1M13DT12H41M55S