0

I'm trying to find a time in a String using a regex in Java. Here's the regex:

\d{1,2}?:\d\d(?)(am|pm)??

That should look for 1 or 2 digits, followed by a colon, two more digits, then either "am" or "pm" (irrespective of case).

It mostly works, but if I wrap that entire regex in a capture group, I only get hh:mm. For example, "12:34am" returns just "12:34". No "am".

UPDATE: Full code example

Pattern p = Pattern.compile("\\d{1,2}?:\\d\\d(?)(am|pm)??");
Matcher matcher = p.matcher("12:34AM");
Assert.assertTrue(matcher.find());
Assert.assertEquals("12:34AM", matcher.group());

Anyone have any idea why?

9
  • 1
    You're looking at the wrong groups. Try group 0. Next time, please post an minimal reproducible example, not just the bits you think are relevant. Guessing at what you're doing is not optimal Commented Oct 22, 2016 at 4:54
  • 1
    Why are there so many question marks? Am I missing something or should this just be \d{1,2}?:\d\d(am|pm) or if the am/pm is optional, \d{1,2}:\d\d(am|pm)? Commented Oct 22, 2016 at 5:00
  • @3ocene I don't believe it's a valid regex as it is anyway. What is (?)? Commented Oct 22, 2016 at 5:02
  • I'm not familiar with the Java regex engine so I was thinking it might have made the next group non-capturing but after googling it seems that it's the same as any other engine: \d{1,2}:\d\d(?:am|pm) Commented Oct 22, 2016 at 5:04
  • @Tibrogargan, I've updated the code with a full example for my original question. I'll now try incorporating the various changes mentioned to see if I can get to the bottom of this. :) Commented Oct 22, 2016 at 5:09

4 Answers 4

3

Instead of your current pattern, you might want to use \b(?:[01]?\d|2[0-3]):[0-5]?\d(?:[ap]m)?\b to match times, with the case-insensitive flag set or even use alternatives to distinct between 24h notation and 12h notation with am/pm

Your current pattern uses a lazy optional pattern to match the am/pm ((am|pm)??), as this is at the end of the pattern, it will never match (except when used in the context of .matches() instead of .find(), as it forces a full match).

I'm honestly not sure about the (?) in your pattern.

Sign up to request clarification or add additional context in comments.

2 Comments

I think this might actually do it, more or less. I tried the regex unchanged and failed to find matches, but if I use (?:[01]?\\d|2[0-3]):[0-5]?\\d *(?:am|AM|pm|PM)?, it works as expected. I'm not a real big fan of the AM/PM handling at the end, but I do have it capturing that now, which is my primary concern, so I can optimize that as time permits.
Yep, you have to use double backslashes for the actual use, but as you wrote your regex pattern in standard notation in your question, I thought it would be fine to use.
1

As you are matching against 12 hour format so there are many bound to cover so never use \d because it will accept any number number input given by user and your regex will fail against these invalid cases like 13:32am 22:23am or many others non-12 hour formats

so to make it robust use

\\b(1[012]|0[1-9]):([0-5][0-9])(\\s)?([Aa]|[pP])[mM]

\\b(1[012]|0?[1-9]): \\b word boundary then either 10,11,12 or 0 with 1-9

:([0-5][0-9]) : character match digits from 00-59

(\\s)? there can be a single space

([Aa]|[pP])[mM] either am,AM or pm,PM etc

Complete code would be

    Pattern pattern=Pattern.compile("\\b(1[012]|0[1-9]):([0-5][0-9])(\\s)?([Aa]|[pP])[mM]");
    Matcher matcher=pattern.matcher("12:23am 12:6am  ds  13:32am dwdw c 01:12am ded 1:21am");

    while (matcher.find()) {
        System.out.println(matcher.group());
    } 

To make it more flexible for inputs like 1:21am and 1:1am use

\\b(1[012]|0?[1-9]):([0-5]?[0-9])(\\s)?([Aa]|[pP])[mM]

Comments

0

This gets your group correctly with modifications to the test string to match case, remove extraneous question marks and to add the surrounding group.

Pattern p = Pattern.compile("(\\d{1,2}:\\d\\d(AM|PM)?)");
Matcher matcher = p.matcher("12:34AM");
Assert.assertTrue(matcher.find());
Assert.assertEquals("12:34AM", matcher.group());

Comments

-1

Sorry i edited: \d?\d:\d?\d["a"|"p"|"A"|"P"]["m"|"M"]

1 Comment

This doesn't seem to really answer the original question. Yes, it may match a time, but it will also match many other things like 1234567890:0987654321|"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.