Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching hours/minutes/seconds in regular expressions - a better way?

So I need to get hours, minutes and seconds out of entries like these:

  • 04:43:12
  • 9.43.12
  • 1:00
  • 01.04
  • 59
  • 09

The first two is hours, minutes and seconds. Next to is minutes and seconds. Last two is just seconds.

And I came up with this regexp, that works..:

\A(?<hours>\d{1,2})(?::|\.)(?<minutes>\d{1,2})(?::|\.)(?<seconds>\d{1,2})\z|\A(?<minutes>\d{1,2})(?::|\.)(?<seconds>\d{1,2})\z|\A(?<seconds>\d{1,2})\z

But it is ugly, and I want to refactor it down to not be 3 different expressions (mostly just to learn). I tried this:

\A(?:(?<hours>\d{1,2})(?::|\.){0,1})(?:(?<minutes>\d{1,2})(?::|\.){0,1})(?:(?<seconds>\d{1,2}){0,1})\z

But that does not work - minutes and seconds sometimes get screwed up. My brain is hurting, and I can't figure out, what I am doing wrong.

like image 238
Kjensen Avatar asked Sep 09 '09 15:09

Kjensen


Video Answer


1 Answers

My suggestion:

(?:(?:(?<hh>\d{1,2})[:.])?(?<mm>\d{1,2})[:.])?(?<ss>\d{1,2})

structured:

(?:                     # group 1 (non-capturing)
  (?:                   #   group 2 (non-capturing)
    (?<hh>\d{1,2})      #     hours
    [:.]                #     delimiter
  )?                    #   end group 2, make optional
  (?<mm>\d{1,2})        #   minutes
  [:.]                  #   delimiter
)?                      # end group 1, make optional
(?<ss>\d{1,2})          # seconds (required)

If you wish, you can wrap the regex in delimiters - like word boundaries \b or string anchors (^ and $).

EDIT: Thinking about it, you can restrict that further to capture times that make sense only. Use

[0-5]?\d

in place of

\d{1,2}

to capture values between 0 and 59 only, where appropriate (seconds and minutes).

like image 162
Tomalak Avatar answered Sep 23 '22 19:09

Tomalak