0

So basically I have a value of number of seconds which I want to represent in ISO8601 format.

For example:

90 seconds would be represented as T1M30S

So far I have done the following:

$length = 90;
$interval = DateInterval::createFromDateString($length . ' seconds');
echo($interval->format('TH%hM%iS%s'));

The output for this is:

TH0M0S90


Ended up building this function which seems to generate the values I need (it is however limited to a time duration that are less than a day:

public function DurationISO8601(){

        $lengthInSeconds = $this->Length;
        $formattedTime = 'T';

        $units = array(
            'H' => 3600,
            'M' => 60,
            'S' => 1
        );

        foreach($units as $key => $unit){
            if($lengthInSeconds >= $unit){
                $value = floor($lengthInSeconds / $unit);
                $lengthInSeconds -= $value * $unit;
                $formattedTime .= $value . $key;
            }
        }

        return $formattedTime;
    }

Thanks

Gabriel Spiteri
  • 4,896
  • 12
  • 43
  • 58
  • 2
    So what's your question? And before you say, "how do I do it?", remember that questions like that are too broad and will be closed as such. So if you've written any code thus far I highly recommend adding it to your question. You should read "[how do I ask a good question?](http://stackoverflow.com/help/how-to-ask)". – John Conde Jun 18 '15 at 13:36
  • You get the number of minutes by dividing by 60 and flooring the result, you get the seconds with the `%` operator, you put it together with a bit of string manipulation... – deceze Jun 18 '15 at 13:39
  • Updated question ... @deceze what if you have more than 60 minutes then? – Gabriel Spiteri Jun 18 '15 at 13:43
  • hmm i got a function for this, but i can't post the answer – PHPhil Jun 18 '15 at 13:57
  • It seems to me this question is legitimate. That just "how to convert a timestamp to a an ISO8601 duration". Informations on the specs on wikipedia: https://en.wikipedia.org/wiki/ISO_8601#Durations – Mat Jun 18 '15 at 14:10
  • *"what if you have more than 60 minutes then"* – then you apply the same logic again... – deceze Jun 18 '15 at 14:15
  • For PHP to parse the string (=> probably defined like this in the standard) it always needs to start with the `P` (period), i.e. `PT1M30S` – Tobias K. Jan 09 '23 at 09:16

1 Answers1

4

Here is what appears to be an ISO8601 duration string generator. It has a bunch of ugly junk to catch starting with a P versus a T depending on duration scope as well as handling a zero second interval.

function iso8601_duration($seconds)
{
  $intervals = array('D' => 60*60*24, 'H' => 60*60, 'M' => 60, 'S' => 1);

  $pt = 'P';
  $result = '';
  foreach ($intervals as $tag => $divisor)
  {
    $qty = floor($seconds/$divisor);
    if ( !$qty && $result == '' )
    {
      $pt = 'T';
      continue;
    }

    $seconds -= $qty * $divisor;    
    $result  .= "$qty$tag";
  }
  if ( $result=='' )
    $result='0S';
  return "$pt$result";
}

A bit of test code to drive the function around the block a couple times:

$testranges = array(1, 60*60*24-1, 60*60*24*2, 60*60*24*60);
foreach ($testranges as $endval)
{
  $seconds = mt_rand(0,$endval);
  echo "ISO8601 duration test<br>\n";
  echo "Random seconds: " . $seconds . "s<br>\n";

  $duration = iso8601_duration($seconds);
  echo "Duration: $duration<br>\n";
  echo "<br>\n";
}

Output from the test code looks similar to this:

ISO8601 duration test
Random seconds: 0s
Duration: T0S

ISO8601 duration test
Random seconds: 3064s
Duration: T51M4S

ISO8601 duration test
Random seconds: 19872s
Duration: T5H31M12S

ISO8601 duration test
Random seconds: 4226835s
Duration: P48D22H7M15S

You might note that I'm a little unsure of determining what a duration of months represents since actual months are not all the same size. I've only calculated from days down so if you have a long duration you'll still only see days at the high end.

A Smith
  • 621
  • 1
  • 4
  • 10
  • The question author asked for no leading `P` so this answer is correct. If a more compliant solution is required there is also a function in Carbon: `\Carbon\CarbonInterval::seconds($seconds)->cascade()->spec()` - source: https://stackoverflow.com/a/73359568/7362396 – Tobias K. Jan 09 '23 at 09:17