I'm working on a Rails application which uses an external feed for some event data and annoyingly they only provide a string with the time in. For example:
Doors open at 7:30pm, show starts at 9pm
I'm aiming to extract the first time from these strings and put it into a datetime field. The system needs to capture the following kinds of values:
But not these ones:
I figure the best way to do this is with regex and through some searching (and particularly this SO question) I've got the following:
[0-9]{1,2}(:|.)??[0-9]{0,2}\s?(am|pm|AM|PM)
It partly works but doesn't exclude any of the ones I don't want and seems to only capture the first character of am/pm in 2 and 3.
Is this possible with regex?
Thanks!
\b((?:0?[1-9]|1[0-2])(?!\d| (?![ap]))[:.]?(?:(?:[0-5][0-9]))?(?:\s?[ap]m)?)\b
It doesn't support 24-hour format but it enforces valid times. Add a case insensitive flag to your regex engine, whatever language it may be, or wrap the regex with (i: )
if it is supported.
Demo with your sample
Regex:
NODE EXPLANATION
--------------------------------------------------------------------------------
\b the boundary between a word char (\w) and
something that is not a word char
--------------------------------------------------------------------------------
( group and capture to \1:
--------------------------------------------------------------------------------
(?: group, but do not capture:
--------------------------------------------------------------------------------
0? '0' (optional (matching the most
amount possible))
--------------------------------------------------------------------------------
[1-9] any character of: '1' to '9'
--------------------------------------------------------------------------------
| OR
--------------------------------------------------------------------------------
1 '1'
--------------------------------------------------------------------------------
[0-2] any character of: '0' to '2'
--------------------------------------------------------------------------------
) end of grouping
--------------------------------------------------------------------------------
(?! look ahead to see if there is not:
--------------------------------------------------------------------------------
\d digits (0-9)
--------------------------------------------------------------------------------
| OR
--------------------------------------------------------------------------------
' '
--------------------------------------------------------------------------------
(?! look ahead to see if there is not:
--------------------------------------------------------------------------------
[ap] any character of: 'a', 'p'
--------------------------------------------------------------------------------
) end of look-ahead
--------------------------------------------------------------------------------
) end of look-ahead
--------------------------------------------------------------------------------
[:.]? any character of: ':', '.' (optional
(matching the most amount possible))
--------------------------------------------------------------------------------
(?: group, but do not capture (optional
(matching the most amount possible)):
--------------------------------------------------------------------------------
(?: group, but do not capture:
--------------------------------------------------------------------------------
[0-5] any character of: '0' to '5'
--------------------------------------------------------------------------------
[0-9] any character of: '0' to '9'
--------------------------------------------------------------------------------
) end of grouping
--------------------------------------------------------------------------------
)? end of grouping
--------------------------------------------------------------------------------
(?: group, but do not capture (optional
(matching the most amount possible)):
--------------------------------------------------------------------------------
\s? whitespace (\n, \r, \t, \f, and " ")
(optional (matching the most amount
possible))
--------------------------------------------------------------------------------
[ap] any character of: 'a', 'p'
--------------------------------------------------------------------------------
m 'm'
--------------------------------------------------------------------------------
)? end of grouping
--------------------------------------------------------------------------------
) end of \1
--------------------------------------------------------------------------------
\b the boundary between a word char (\w) and
something that is not a word char
Perhaps something like this:
^[01]?[0-9]([:.][0-9]{2})?(\s?[ap]m)?$
Demonstration
Note that this will not handle 24-hour time, and it's not that specific about 12-hour time—i.e. it would match 19pm
.
If you want to be more specific, you might try:
^((0?[0-9]|1[012])([:.][0-9]{2})?(\s?[ap]m)|([01]?[0-9]|2[0-3])([:.][0-9]{2})?)$
Demonstration
Or to try to match it as part of a larger portion of text, you might use something like this:
\b((0?[1-9]|1[012])([:.][0-5][0-9])?(\s?[ap]m)|([01]?[0-9]|2[0-3])([:.][0-5][0-9]))\b
Demonstration
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With