3

I am trying to validate date inputs, and I only want them to pass if the day and the month are always two digits. So far I've been doing this with SimpleDateFormat:

    SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
    df.setLenient(false);

    try
    {
        df.parse("10/1/1987");
        df.parse("1/1/1987");
        df.parse("1/10/1987");
        df.parse("1/1/19");
    }

    catch (ParseException e)
    {
        e.printStackTrace();
    }

All of those cases are passing though, and I don't want any of them to pass.

Is there an easy way to fix this, or will I have to tokenize the string on backslashes first with something like:

String[] dateParts = date.split("/");
if (dateParts.length != 3 && dateParts[0].length() != 2 && dateParts[1].length() != 2 && dateParts[2].length() != 4)
    System.out.println("Invalid date format");
Jimmy P
  • 1,580
  • 2
  • 13
  • 29
  • 2
    You could use a [Pattern](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html) [Matcher](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html), and match the appropriate regular expression ... such as `"\d{2}/\d{2}/\d{4}"`. Eg) `if (date.matches("\\d{2}/\\d{2}/\\d{4}")) { ... }` – AJNeufeld Dec 15 '16 at 17:43
  • you can use Regex, something like ([0-9]{2})/([0-9]{2})/([0-9]{4}) – dreamer Dec 15 '16 at 17:44
  • Did you get it working? – ItamarG3 Dec 15 '16 at 18:17
  • @ItamarGreen Yes, thank you. – Jimmy P Dec 15 '16 at 18:23
  • @JimmyP then you should accept one of the answers, the one that helped you get it working – ItamarG3 Dec 15 '16 at 18:27

3 Answers3

3

Use the new java.time instead:

public static void main(String[] args) {
    test("10/10/1987");
    test("10/1/1987");
    test("1/1/1987");
    test("1/10/1987");
    test("1/1/19");
}
private static void test(String date) {
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/uuuu");
    try {
        System.out.println(LocalDate.parse(date, fmt));
    } catch (Exception e) {
        System.out.println(e);
    }
}

Output

1987-10-10
java.time.format.DateTimeParseException: Text '10/1/1987' could not be parsed at index 3
java.time.format.DateTimeParseException: Text '1/1/1987' could not be parsed at index 0
java.time.format.DateTimeParseException: Text '1/10/1987' could not be parsed at index 0
java.time.format.DateTimeParseException: Text '1/1/19' could not be parsed at index 0
Andreas
  • 138,167
  • 8
  • 112
  • 195
  • What's the difference between "uuuu" and "yyyy"? – Jimmy P Dec 15 '16 at 18:10
  • @JimmyP Why don't you have a look at the [javadoc](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) and see for yourself? – Andreas Dec 15 '16 at 18:13
  • 1
    I did. In the table of symbols both have the same Presentation and Example, and the explanation for Year doesn't distinguish between them. The only difference I see is that one is for "year" and the other is for "year-of-era", and I'm not sure what exactly that means. – Jimmy P Dec 15 '16 at 18:22
  • 1
    Do you understand `day-of-week`, `day-of-month`, and `day-of-year`, i.e. the day number within the given scope (week, month, year)? So, `year-of-era` means the year number within the given scope (era), and right above it `era` is shown with an example value of `AD` (the other value of course being `BC`). So, when was Julius Caesar assassinated? March 15, 44 BC (`MMMM d, y GG`) *or* March 15, -43 (`MMMM d, u`)? If you use `y` you should also use `G`. Since `G` is rarely used, the correct year symbol is `u`, not `y`. – Andreas Dec 15 '16 at 18:59
  • @Andreas I just posted [a Question on this matter of `uuuu` versus `yyyy`](http://stackoverflow.com/q/41177442/642706). I found no other Question touching explicitly on this matter. Perhaps you would like to post a formal Answer there. – Basil Bourque Dec 16 '16 at 04:50
1

One way would be to validate the input, ensure it is matching the desired format before you parse it.

if (date.matches("\\d{2}/\\d{2}/\\d{4}")) {
    SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
    df.parse(date);
}
AJNeufeld
  • 8,231
  • 1
  • 22
  • 40
  • Would a regex be faster than splitting the string and checking that way though? – Jimmy P Dec 15 '16 at 18:09
  • Readability or speed? If you split the string on the `/` character, you'd need to check that (a) the string split into exactly 3 parts, (b) that each part contained only digits, (c) that the first two parts are of length 2 and the third part is of length 4. The regex has done all of that, in a simple, clear, readable if statement. If speed is a concern, the regex could be compiled once and stored as a `final static Matcher`. – AJNeufeld Dec 15 '16 at 18:18
  • If we're talking about a single line of code I'd be more concerned about speed than readability, but does it really matter for a string that's only 10 characters? – Jimmy P Dec 15 '16 at 18:26
  • If you are concerned about the speed of regex, how are you planning on splitting the string? [String#split(String regex)](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split(java.lang.String)) defines the split points via a regex. – AJNeufeld Dec 15 '16 at 18:33
1

You can use regex,

String regex = "(\\d{2})\\/(\\d{2})\\/(\\d{2,4})$";
Pattern p = Pattern.compile(regex);
String input = "10/12/1987";
Matcher m = p.matcher(input);
while (m.find()) {
    //if it got here, then the date is in the right format
    System.out.println(m.group(0));
}

The pattern checks for 2 digits (\\d{2} matches digits, exactly 2 times) followed by a slash (\/) and then again the 2 digits and then again followed by a slash, and then 2-4 numbers (the year).

ItamarG3
  • 3,846
  • 6
  • 28
  • 42