php – Convert seconds to timestamp?

Question:

I'm creating a PHP script to generate a json from a webvtt subtitle webvtt , I get the beginning and end that are in the format minuto:segundo.milisegundo or if the video is too big they come as hora:minuto:segundo.milisegundo for future comparisons in JS in the future where the video is playing I need to compare these values ​​with the currentTime of the video which in turn gives the time in seconds segundos.milisegundos and to facilitate such comparison I would like my PHP to already deliver the time beginning and end of each subtitle already in the same format as the currentTime of the video, how can I do it?

Here's an example:

$start = "00:05.570";
$fim = "00:09.700";

In the example above it would be easy to distinguish that the currentTime would be 5.570 and 9.700 how do I get PHP to convert to this format?

Answer:

Solution

A solution using regular expressions would be, making just the required seconds value:

function convert($value)
{
    if (preg_match("/(((?P<hours>\d+)\:)?(?P<minutes>\d{1,2})\:)?(?P<seconds>\d{1,2})(\.(?P<milis>\d+))?/", $value, $matches))
    {
        $hours   = intval($matches["hours"]);
        $minutes = intval($matches["minutes"]);
        $seconds = intval($matches["seconds"]);
        $milis   = isset($matches["milis"]) ? intval($matches["milis"]) : 0;

        return sprintf("%d.%d", $hours * 3600 + $minutes * 60 + $seconds, $milis);
    }

    return false;
}

// Entrada: horas:minutos:segundos.milis
echo convert("123:12:42.9"), PHP_EOL;   // 443562.9

// Entrada: horas:minutos:segundos.milis
echo convert("01:20:03.7345"), PHP_EOL; // 4803.7345

// Entrada: horas:minutos:segundos.milis
echo convert("0:01:56.23"), PHP_EOL;    // 116.23

// Entrada: minutos:segundos.milis
echo convert("00:05.570"), PHP_EOL;     // 5.570

// Entrada: minutos:segundos.milis
echo convert("00:09.700"), PHP_EOL;     // 9.700

// Entrada: minutos:segundos
echo convert("00:05"), PHP_EOL;         // 5.0

// Entrada: segundos.milis
echo convert("4.55"), PHP_EOL;          // 4.55

// Entrada: segundos
echo convert("12"), PHP_EOL;            // 12.0

See working on Ideone .


Explanation

The solution was entirely based on PHP's native preg_match :

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

The first parameter, $pattern , is the regular expression we're going to look at. The second, $subject , is the string to which we will apply the regular expression and the third parameter, $matches , will be an array of string values ​​that match the pattern defined in the regular expression.

The regular expression used is divided into four parts:

/(((?P<hours>\d+)\:)?(?P<minutes>\d{1,2})\:)?(?P<seconds>\d{1,2})(\.(?P<milis>\d+))?/
  +-----------------+-----------------------+-------------------+------------------+
      (horas)               (minutos)             (segundos)          (milis)

regular expression: hours

The regular expression for the hours, ((?P<hours>\d+)\:)? , can be reduced to (\d+\:)? , which means one or more digits ( \d+ ) followed by an optional : ( \: ) character ( ? ). The ?P<hours> is only for naming the group; that is, if there is a value that $matches this pattern, create in $matches the hour index with the hour value. For example, if the entry is 01:20:03.7345 , there would be $matches["hours"] equal to 01 . If the hour is not defined, $matches["hours"] will be false (since we defined it as optional in the regular expression).

regular expression: minutes

See hours (exactly the same logic, just changing the group name to minutes , ((?P<minutes>\d{1,2})\:)? ). We also changed the quantifier from + to {1,2} , because the minutes will have 1 or 2 digits: 1 if it is less than 10 minutes (considering that the 0 to the left may not be added) or 2 digits when 10 to 59 minutes.

regular expression: seconds

It's basically the same expression used for the minutes, except that it will be mandatory, so there won't be the ? at the end, (?P<seconds>\d{1,2}) .

regular expression: millis

For milliseconds, (\.(?P<milis>\d+))? , stays: if defined ( ? ), it must start with the character. ( \. ) followed by one or more digits ( \d+ ), capturing this group with the name of milis . Note that the \. it is not part of the named group as we only want the numeric value, otherwise $matches["milis"] would be something like .570 instead of 570 .

Scroll to Top