Take the 2-minute tour ×

A library and webservice I am using, communicates time-intervals in ISO 8601: PnYnMnDTnHnMnS. I want to convert such formats to seconds. And vice versa. Seconds are a lot easier to calculate with.

Example interval values are:

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

I need two functions: parse from such values to seconds: iso8601_interval_to_seconds() and from seconds into such intervals: iso8601_interval_from_seconds().

The latter is rather simple, because it could be done as `"PT{$seconds}S", just pass seconds along, at all times. Maybe this can be done nicer with a parser that switches to H(hour) or M(minute)?

The first is harder, but maybe there is a trick with one of the many string-to-date converters in PHP? I would love to learn how to use such a function for parsing intervals. Or learn an alternative.

share|improve this question

4 Answers 4

up vote 9 down vote accepted

It looks like PHP 5.3's DateInterval supports this.

If you can't use 5.3, I suppose any such conversion function would know how many seconds are in a year, a month, a day, an hour, and a minute. Then, when converting from seconds, it would divide in that order, each time taking the modulo of the previous operation, until only <60 seconds are left. When converting from an ISO 8601 interval representation it should be trivial to parse the string and multiply each found element accordingly.

share|improve this answer
DateInterval('PT1H') works likea charm! Now, I might want to look into a nicer way to rebuild these strings, other then PTXXXS, everyting in second :) –  berkes Sep 15 '10 at 21:04

Be aware that converting durations that contain Days, Months, and/or Years into a duration like seconds can not be done accurately without knowing an actual starting date/time.

For Example

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

That's because September has 30-days, and October has 31-days. The same problem occurs with converting Year intervals, because of leap-years and leap-seconds. Leap-years introduce further complexity to the Month conversion as well - since February is one day longer in leap-years than it is otherwise. Days are problematic in areas where daylight saving time is practiced - a one-day period occurring during the DST transition, is actually 1-hour longer than it would be otherwise.

All that being said -- you can, of course, compress values containing only Hours, Minutes, and Seconds into values containing just Seconds. I'd suggest that you build a simple parser to do the job (or maybe consider a regular expression).

Just be aware of the pitfalls outlined above -- there there be dragons. If you intend to deal with Days, Months, and/or Years, you need to use one of the built-in mechanisms to do the math for you in the context of a known date/time. As others have mentioned: the DateInterval class, in combination withe the functions provided on the DateTime class is probably the most intuitive way to deal with this. But that's only available in PHP version 5.3.0 or greater.

If you have to work with less than v5.3.0, you can try to build something around this little gem:

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

strtotime won't work with the ISO 8601 format directly (eg. P1Y1DT1S), but the format that it does understand (1Year1Day1Second) is not too far off -- it would a pretty straight-forward conversion. (a little "hacky"... but that's PHP for you).

good luck!

share|improve this answer
Thinking about this intuitively I'd expect that rewriting an absolute number of seconds as years + months + days + hours + minutes + seconds to make it more readable will not change its actual value, meaning that wherever there's a variable interval, the standard value will be chosen. So, for months, that would be 30 days = 1 calendar month. I'm trying to find where exactly they specify this in the standard itself but it's getting late, and I can only see the final draft without paying: xml.coverpages.org/ISO-FDIS-8601.pdf –  Fanis Sep 15 '10 at 21:57
At first it does seem that that would be the intuitive answer, but if you really think about it, it becomes apparent that things really can't work like that. Turns out that "calendar time" (Days, Months, Years) is just as important as "absolute time" (Seconds, Minutes, Hours) -- when we use calendar time, we mean something fundamentally different than when we use absolute time. For example: How far apart are your birthdays? Always 1-year, not always 8760-hours. If last month's credit card bill was dated Aug 21, what will be the date of this month's? Sept 21. Always 1-month, not 30-days. –  Lee Sep 16 '10 at 1:00


Thanks Lee, I was not aware strtotime accepted this format. This was the missing part of my puzzle. Perhaps my functions can complete your answer.

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);
        // 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; 
        return false;

The function also supports negative formats. -P10Y9MT7M5S will return an array like: [year] => -10 [month] => -9 [day] => 0 [hour] => 0 [minute] => -7 [second] => -5 If this behaviour is not desired pass false as second parameter. This way the function will always return positive values. The min/plus symbol will still be available in result key ['symbol'].

And a little update: This function uses the first function to get the total amount of seconds.

function get_duration_seconds($iso_duration){
    // Get duration parts
    $duration = parse_duration($iso_duration, false);
        $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);
        // 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
share|improve this answer

What you are looking for is DateTime::diff

The DateInterval object representing the difference between the two dates or FALSE on failure as illustrated below:

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

Just use seconds instead of dates.

This is from http://www.php.net/manual/en/datetime.diff.php

For a reference to DateInterval see http://www.php.net/manual/en/class.dateinterval.php

share|improve this answer
I fail to see how this helps to convert to and fro the PT10M -alike ISO 8601 formats. –  berkes Sep 15 '10 at 20:58

Your Answer


