3

I am doing the following programming exercise: regex validation of 24 hours time. The statement is:

Write a regex to validate a 24 hours time string. See examples to figure out what you should check for:

Accepted: 01:00 - 1:00

Not accepted:

24:00

You should check for correct length and no spaces.

I would like to ask, why the following regular expression, matches 24:00

public class RegexValidation {
  public static boolean validateTime(String time) {
    System.out.println("time: "+time);
    return time.matches("(?:^[2])[0123]+:[0-5]+[0-9]|[0-9]+:[0-5]+[0-9]");
  }
}

Because I thought it was expressing: "If string starts with 2, then it should continue with [0-3] (one or more) : [0-5] one or more and [0-9]. Else it continues with [0-9] one or more : [0-5] one or more and [0-9]."

The tests are:

import org.junit.Test;
import static org.junit.Assert.*;

public class RegexValidationTest {

  @Test
  public void test1() {
    assertTrue(RegexValidation.validateTime("01:00"));
  }

  @Test
  public void test2() {
    assertTrue(RegexValidation.validateTime("1:00"));
  }

  @Test
  public void test3() {
    assertTrue(RegexValidation.validateTime("00:00"));
  }

  @Test
  public void test4() {
    assertFalse(RegexValidation.validateTime("13:1"));
  }

  @Test
  public void test5() {
    assertFalse(RegexValidation.validateTime("12:60"));
  }

  @Test
  public void test6() {
    assertFalse(RegexValidation.validateTime("24:00"));
  }
}

In addition I wrote the following solution, which passes the tests:

public class RegexValidation {
  public static boolean validateTime/*⌚*/(String time) {
    if(time.matches("[\\d]+:[\\d]{2}")){
      String[] times = time.split(":");
      int hours = Integer.parseInt(times[0]);
      int minutes = Integer.parseInt(times[1]);
      return hours < 24 && minutes < 60;
    }
    return false;
  }
}

I have also read:

Finally the question is, why in the first code the regex matches 24:00?‽

Yone
  • 2,064
  • 5
  • 25
  • 56
  • The pattern matches `24:00` due to the alternation `|`. If the first part does not match, then `[0-9]+:[0-5]+[0-9]` will match `24:00` The pattern can be written as `^2[0123]+:[0-5]+[0-9]|[0-9]+:[0-5]+[0-9]` and will also match for example `24:59` or `99:50` but not `24:60` – The fourth bird Dec 29 '19 at 09:06
  • See https://stackoverflow.com/questions/1494671/regular-expression-for-matching-time-in-military-24-hour-format for possible solutions. – The fourth bird Dec 29 '19 at 09:22

1 Answers1

3

This is because of the | present. The alternative after this, [0-9]+ matches any digit from 1 to 9, and breaks the required output.

The following regex should work,

^([01]\d|[0-9]|2[0-3]):?([0-5]\d)$

If the : is not optional, remove the ?

^([01]\d|[0-9]|2[0-3]):([0-5]\d)$

Also verified on jshell

jshell> var pattern = Pattern.compile("^([01]\\d|[0-9]|2[0-3]):?([0-5]\\d)$");
pattern ==> ^([01]\d|[0-9]|2[0-3]):?([0-5]\d)$

jshell> pattern.matcher("23:01").matches();
$2 ==> true

jshell> pattern.matcher("24:01").matches();
$3 ==> false

jshell> pattern.matcher("00:01").matches();
$4 ==> true

jshell> pattern.matcher("09:01").matches();
$5 ==> true

jshell> pattern.matcher("9:01").matches();
$6 ==> true

jshell> pattern.matcher("12:00").matches();
$7 ==> true
Adwait Kumar
  • 1,552
  • 10
  • 25