1

The homework assignment says I have to write code that validates dates in yyyy/mm/dd format. It asks that I first check the input String so that the first part, the year is exactly 4 numbers and month is between 1 and 2 (inclusive). And if it doesn't match that criteria, i throw an exception called "InvalidDateException", (I've already wrote the class for that)

So my given example is 2016/05/12 should be considered a valid date.

Looking into how Regex works, I come to the conclusion that I would need \\d+ so that java can find numbers.

Here's my code (variable date is a String, instantiated in the method that contains this code):

int yr = date.substring(0, date.indexOf("/")).length();
int mo = date.substring(date.indexOf("/")+1, date.lastIndexOf("/")).length();

if (date.matches("\\d+") && (yr == 4) && (mo <= 2 && mo >= 1)) {
//I've put code here that outputs it as a valid date
}

So then if I put 2016/05/12, it should say that it's a valid date, however it instead goes to my error message of "InvalidDateException"

I've looked through the other regex questions on StackOverflow but I can't seem to find out why my code doesn't work.

any help is appreciated

Schnitzel
  • 75
  • 5
  • 1
    do you have to use regex? – Scary Wombat Mar 28 '18 at 00:36
  • 1
    is there a way of doing this without the regex? – Schnitzel Mar 28 '18 at 00:37
  • 1
    Why don't you split the date by '/' and check the values separately? – Douglas Mar 28 '18 at 00:40
  • you could use `String::split` to split (split on '/') and then evaluate this three elements testing to see if is a `int` - see https://stackoverflow.com/a/5439547/2310289 – Scary Wombat Mar 28 '18 at 00:40
  • \\d+ means that all characters in string should be numbers. But your string is `2016/05/12`. – uli Mar 28 '18 at 00:42
  • That would be how I would want to solve this by. However our professor said that we have to do this way, where we take the String input , check that it has 4 numbers for the year and between 1 and 2 inclusive for the month – Schnitzel Mar 28 '18 at 00:42
  • @uli so how would i go about making a regex that would check to see that there are 3 groups of digits in the input date String ? – Schnitzel Mar 28 '18 at 00:44
  • 1
    Never mind i figured it out. So what i instead did was separate the year part (the first 4 digits) into a String with substring(), then the month part into another substring, then used the `\\d+` to check if they were numbers. After that it worked correctly. thanks all – Schnitzel Mar 28 '18 at 00:51
  • @Schnitzel, good catch! This is one of possible solution. – uli Mar 28 '18 at 01:01

2 Answers2

2

Example 1

This would give you a straight-forward solution but you'd still need to parse the String for its components.

Pattern pattern = Pattern.compile("\\d{4}/\\d{1,2}/\\d{1,2}");
Matcher matcher = pattern.matcher(input);

if (matcher.matches())
{
    // Some code here to extract the year/month/day...
}

Example 2

A better way is to group the results (with parenthesis).

Pattern pattern = Pattern.compile("(\\d{4})/(\\d{1,2})/(\\d{1,2})");
Matcher matcher = pattern.matcher(input);

if (matcher.matches())
{
    int year = Integer.valueOf(matcher.group(1));  // First group
    int month = Integer.valueOf(matcher.group(2)); // Second group
    int day = Integer.valueOf(matcher.group(3));   // Third group

    // Some code here...
}

Example 3

An even better way is to name the grouped results.

Pattern pattern = Pattern.compile("(?<year>\\d{4})/(?<month>\\d{1,2})/(?<day>\\d{1,2})");
Matcher matcher = pattern.matcher(input);

if (matcher.matches())
{
    int year = Integer.valueOf(matcher.group("year"));   // "year" group
    int month = Integer.valueOf(matcher.group("month")); // "month" group
    int day = Integer.valueOf(matcher.group("day"));     // "day" group

    // Some code here...
}

Example 4.a

You could use a regex builder class. This is beneficial as it has some re-usability.

public class Regex {
    private StringBuilder regexBuilder = new StringBuilder();
    private final String input;

    private Regex(String input) {
        this.input = input;
    }

    public static Regex of(String input) {
        return new Regex(input);
    }

    public Regex append(String regex) {
        regexBuilder.append(regex);
        return this;
    }

    public Regex group(String groupName, String regex) {
        regexBuilder.append("(?<")
                    .append(groupName)
                    .append(">")
                    .append(regex)
                    .append(")");

        return this;
    }

    public Matcher matcher() {
        return Pattern.compile(regexBuilder.toString()).matcher(input);
    }
}

Example 4.b

Using the builder...

final String yearGroup = "year";
final String monthGroup = "month";
final String dayGroup = "day";
Matcher matcher = 
        Regex.of(input)
             .group(yearGroup, "\\d{4}")
             .append("/")
             .group(monthGroup, "\\d{1,2}")
             .append("/")
             .group(dayGroup, "\\d{1,2}")
             .matcher();

if (matcher.matches())
{
    int year = Integer.valueOf(matcher.group(yearGroup));   // "year" group
    int month = Integer.valueOf(matcher.group(monthGroup)); // "month" group
    int day = Integer.valueOf(matcher.group(dayGroup));     // "day" group

    // Some code here...
}
-1

Split solution is good if you need to make further complicated validations.

public boolean isDateValid (String date) {
    String[] dateElements = date.split("/");
    if (dateElements.length == 3)
        return isDateElementsValid(dateElements);
    else
        throw new RuntimeException();
}

public boolean isDateElementsValid(String[] dateElements){
    String year = dateElements[0];
    String month = dateElements[1];
    String day = dateElements[2];
    return isYearValid(year) && isMonthValid(month) && isDayValid(day);
}

Regex is good to have less code.

public boolean isDateValid (String date) {
    if (date.matches("\\d{4}/\\d{1,2}/\\d{1,2}"))
        return true;
    else
        throw new RuntimeException();
}

*Replace RuntimeException with custom implementation. Best practice will be to extend custom exception from RuntimeException.

Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
uli
  • 671
  • 6
  • 15
  • sarcastic comment and minus instead of simple edit, to correct typo. good strategy! – uli Mar 28 '18 at 02:33