views:

376

answers:

4

I would like to validate and extract the hours and minutes from a string using Regex in .NET. Just to recuperate the two numbers, separated(or not) by :. Accepted format h:m or m. Unaccepted :m, h:.

EDIT: This is to note, that the number of hours could overflow the 23 till... 32.

The overflow for hours(over 32) and minutes(over 59) I will do after values recuperation(int.Parse)


* just for fun maybe there a a relative simple regex that could filter also the >32 for hour and >59 for minute (for minutes it could be [0-5]*[0-9], for hours I don't know)?

+2  A: 

(?:(\d\d?):)?([0-5][0-9])

If you want to validate hours:

(?:([01]?[0-9]|2[0-3]):)?([0-5][0-9])

EDIT: Tested and corrected.


However, the best way to do this is with DateTime.ParseExact, like this: (Tested)

TimeSpan time = DateTime.ParseExact(
    input, 
    new string[] { "HH:mm", "H:mm", "mm", "%m" }, //The % is necessary to prevent it from being interpreted as a single-character standard format.
    CultureInfo.InvariantCulture, DateTimeStyles.None
).TimeOfDay;

For validation, you can use TryParseExact.

SLaks
**:** is *not* mandatory if the hour is not present
serhio
I edited the regexes to reflect that.
SLaks
@SLaks: **:** is always mandatory in your expression.
serhio
I tested the regexes and found some mistakes, which I corrected.
SLaks
@yeah. as other fixes i could mention `DateTime time` and `new string[]`. However thanks, I appreciate the answer and from regex, and from dateTime perspective.
serhio
`time` is a `TimeSpan`. (Note the `TimeOfDay` property) `new [] { }` is C# 3.0 syntax; if you're using C# 2.0, you'll need to write `new string[] { }`.
SLaks
Just **"5"** will not parse the **"m"** DateTime...I used `bool result = DateTime.TryParseExact( "5", new string [] { "HH:mm", "H:mm", "mm", "m" }, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt);`
serhio
You need to use `%m`. See here:http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx#UsingSingleSpecifiers
SLaks
yah bool result = DateTime.TryParseExact( "5", new string [] { "%H:%m", "H:%m", "%m"}, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt); worked!
serhio
Then you should accept this answer.
SLaks
yah, I have one more cause to use regex instead of DateTime.Parse - the number of hours in my case will over 24 -- 32.
serhio
?Regex.IsMatch(**"33:59"**, "(?:([012]?[0-9]|3[01]):)?([0-5][0-9])")**True** - not good...
serhio
+6  A: 

Are you dead set on a regex? Because DateTime.Parse would be much simpler and more powerful here.

 DateTime dt = DateTime.Parse("12:30 AM");

Then dt gives you everything you need to know about the time. DateTime.TryParse() may be better in case you are less certain it's a time string.

Matt Greer
You could have reason. First of all, I need to exclude any local Culture influence. Secondly, my question was linked more to a Regex exercise.
serhio
Then you should pass `CultureInfo.InvariantCulture`. See my answer.
SLaks
Yeah `InvariantCulture` would help you. But if this really is just a means of getting a better feel for regular expressions, then by all means, please ignore my answer then :)
Matt Greer
I forget one little thing: maxhour = 32 :)
serhio
@serhio how do you learn from a Regex exercise if you don't try it yourself...
PoweRoy
+1  A: 

Here's the regex string. You can access the named capture groups "hours" and "minutes". Use flags "ExplicitCapture" and "Singleline".

@"^((?<hours>[0-9]{1,2})\:)?(?<minutes>[0-9]{1,2})$"

You can test regexes here: http://derekslager.com/blog/posts/2007/09/a-better-dotnet-regular-expression-tester.ashx

As mentioned, a DateTime parse call might be better unless you need to validate that only this form is allowed.

Also, negative values aren't permitted, neither are decimals. (But, the regex can be changed to include them if needed).

Computer Linguist
hmm... from your tester site: Error: parsing "^((?[0-9]{1,2})\:)?(?[0-9]{1,2})$" - Unrecognized grouping construct.
serhio
Thanks - fixed it. My angle brackets got lost in translation.
Computer Linguist
useful with explicit capture
serhio
A: 

Finally, the code for validating (till 32) and also obtaining values is(vb.net version):

Dim regexHour As New Regex( _ 
   "((?<hours>([012]?\d)|(3[01]))\:)?(?<minutes>[0-5]?\d)", _
    RegexOptions.ExplicitCapture)
Dim matches As MatchCollection = regexHour.Matches(value)

If matches.Count = 1 Then
  With matches(0)
    ' (!) The first group is always the expression itself. '
    If .Groups.Count = 3 Then ' hours : minutes '
      strHours = .Groups("hours").Value
      If String.IsNullOrEmpty(strHours) Then strHours = "0"
      strMinutes = .Groups("minutes").Value
    Else ' there are 1, 3 or > 3 groups '
      success = False
    End If
  End With
Else
  success = False
End If

Thanks everybody contributing to this answer!

serhio
I think you confused the `?` quantifier (zero or one repetition) with the `*` quantifier (zero or more repetitions).
Gumbo
@Gumbo: Yeah. Thanks! Updated.
serhio