Ituhimux
Ituhimux

Reputation: 47

Time String to Seconds

How can i parse strings with regex to calculate the total seconds? The strings will be in example:

40s
11m1s
1h47m3s

I started with the following regex

((\d+)h)((\d+)m)((\d+)s)

But this regex will only match the last example. How can i make the parts optional? Is there a better regex?

Upvotes: 1

Views: 65

Answers (3)

Piotr
Piotr

Reputation: 504

The format that you are using is very similar to the one that is used by java.time.Duration:

https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-

Maybe you can use it instead of writing something custom?

Duration uses a format like this:

P1H47M3S

Maybe you can add the leading "P", and parse it (not sure if you have to uppercase)?

The format is called "ISO-8601":

https://en.wikipedia.org/wiki/ISO_8601

For example,

$set = array(
    '40s',
    '11m1s',
    '1h47m3s'
);

$date = new DateTime();
$date2 = new DateTime();

foreach ($set as $value) {
  $date2->add(new DateInterval('PT'.strtoupper($value)));
}

echo $date2->getTimestamp() - $date->getTimestamp(); // 7124 = 1hour 58mins 44secs.

Upvotes: 3

Logan Bertram
Logan Bertram

Reputation: 1942

This expression:

/(\d*?)s|(\d*?)m(\d*?)s|(\d*?)h(\d*?)m(\d*?)s/gm

returns 3 matches, one for each line. Each match is separated into the salient groups of only numbers.

The gist is that this will match either any number of digits before an 's' or that plus any number of digits before an 'm' or that plus any number of digits before an 'h'.

Upvotes: 0

Syscall
Syscall

Reputation: 19780

You could use optional non-capture groups, for each (\dh, \dm, \ds):

$strs = ['40s', '11m1s', '1h47m3s'];

foreach ($strs as $str) {
    if (preg_match('~(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?~', $str, $matches)) {
        print_r($matches);
    }
}

Outputs:

Array
(
    [0] => 40s
    [1] =>     // h
    [2] =>     // m
    [3] => 40  // s
)
Array
(
    [0] => 11m1s
    [1] =>     // h
    [2] => 11  // m
    [3] => 1   // s
)
Array
(
    [0] => 1h47m3s
    [1] => 1   // h
    [2] => 47  // m
    [3] => 3   // s
)

Regex:

(?:     # non-capture group 1
  (     # capture group 1
   \d+  # 1 or more number
  )     # end capture group1
  h     # letter 'h'
)       # end non-capture group 1
?       # optional

(?:     # non-capture group 2
  (     # capture group 2
  \d+   # 1 or more number
  )     # end capture group1
  m     # letter 'm'
)       # end non-capture group 2
?       # optional

(?:     # non-capture group 3
  (     # capture group 3
  \d+   # 1 or more number
  )     # end capture group1
  s     # letter 's'
)       # end non-capture group 3
?       # optional

Upvotes: 1

Related Questions