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
.