underhook
underhook

Reputation: 55

Java RegEx Date Validation - Leap Year

I am working on a school assignment and I am just stuck with the logic on this. Basically need to validate a RegEx (date) of years between 1900 and 2099. The days cannot exceed the maximum month days; (ie. June has 30 days so 31 is invalid) as well as account for leap years where the February date can be 29. Here is what I have so far but I keep getting validation errors from when the code is ran in the TestHarness. I think I have way to much logic here but not sure how to resolve. Any help would be awesome.

    public class RegexValidator {
            //class variables
            private String DATE_REGEX = "^((19|20)\\d\\d)-(0?[1-9]|
            1[012])-(0?[1-9]|[12][0-9]|3[01])$";

    public RegexValidator() {
            m_datePattern.compile(DATE_REGEX);
    }

    public boolean validDate(String date) {

        Matcher matcher = m_datePattern.matcher(date);

        int year = Integer.parseInt(matcher.group(1));//parse to calculate leap
        String month = matcher.group(2);
        String day = matcher.group(3);

        if(matcher.matches()) {
            matcher.reset();

            if(matcher.find()) {

                if(day.equals("31") && (month.equals("4") || month.equals("04") ||
                month.equals("6") || month.equals("06") || month.equals("9") ||
                month.equals("09") || month.equals("11"))) {
                    return false;
                } else if(month.equals("2") || month.equals("02")) {
                    //accounts for leap year
                    if(year % 4 == 0) {
                        if(day.equals("30") || day.equals("31")) {
                            return false;
                        } else {
                            return true;
                        }
                    } else {
                        if(day.equals("29") || day.equals("30") || day.equals("31")) {
                            return false;
                        } else {
                            return true;
                        }
                    }
                } else {
                    return true;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }

    }

Here is a list of the errors I am getting:

    Error validating date '1900-1-11'

Error validating date '1900-1-11'

 ---- test date 1892-02-29 - expected false
 ---- Exception calling validDate: null
 ---- test date 1976-02-29 - expected true
 ---- Exception calling validDate: null
Error validating date '1976-02-29'

Error validating date '1976-02-29'

 ---- test date 2001-02-29 - expected false
 ---- Exception calling validDate: null
 ---- test date 2002-13-02 - expected false
 ---- Exception calling validDate: null
 ---- test date 2003-01-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2003-01-31'

Error validating date '2003-01-31'

 ---- test date 1964-03-31 - expected true
 ---- Exception calling validDate: null
Error validating date '1964-03-31'

Error validating date '1964-03-31'

 ---- test date 1965-4-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 1954-4-30 - expected true
 ---- Exception calling validDate: null
Error validating date '1954-4-30'

Error validating date '1954-4-30'

 ---- test date 1901-5-33 - expected false
 ---- Exception calling validDate: null
 ---- test date 1901-5-15 - expected true
 ---- Exception calling validDate: null
Error validating date '1901-5-15'

Error validating date '1901-5-15'

 ---- test date 2016-05-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2016-05-31'

Error validating date '2016-05-31'

 ---- test date 2006-06-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 20016-06-29 - expected false
 ---- Exception calling validDate: null
 ---- test date 1934-7-4 - expected true
 ---- Exception calling validDate: null
Error validating date '1934-7-4'

Error validating date '1934-7-4'

 ---- test date 2016-07-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2016-07-31'

Error validating date '2016-07-31'

 ---- test date 2011-8-19 - expected true
 ---- Exception calling validDate: null
Error validating date '2011-8-19'

Error validating date '2011-8-19'

 ---- test date 2111-8-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 2011-09-30 - expected true
 ---- Exception calling validDate: null
Error validating date '2011-09-30'

Error validating date '2011-09-30'

 ---- test date 2111-9-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 2011-10-1 - expected true
 ---- Exception calling validDate: null
Error validating date '2011-10-1'

Error validating date '2011-10-1'

 ---- test date 2111-10-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 2001-10-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2001-10-31'

Error validating date '2001-10-31'

 ---- test date 2111-10-33 - expected false
 ---- Exception calling validDate: null
 ---- test date 2111-11-30 - expected false
 ---- Exception calling validDate: null
 ---- test date 2013-11-30 - expected true
 ---- Exception calling validDate: null
Error validating date '2013-11-30'

Error validating date '2013-11-30'

 ---- test date 2111-11-31 - expected false
 ---- Exception calling validDate: null
 ---- test date 2011-12-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2011-12-31'

Error validating date '2011-12-31'

 ---- test date 2099-12-31 - expected true
 ---- Exception calling validDate: null
Error validating date '2099-12-31'

Error validating date '2099-12-31'

Upvotes: 2

Views: 1425

Answers (3)

Stéphane GRILLON
Stéphane GRILLON

Reputation: 11884

I have a better solution (with Leap year):

public static final String DATE_FORMAT_REG_EXP = "(^(((0[1-9]|1[0-9]|2[0-8])[\\/](0[1-9]|1[012]))|((29|30|31)[\\/](0[13578]|1[02]))|((29|30)[\\/](0[4,6,9]|11)))[\\/](19|[2-9][0-9])\\d\\d$)|(^29[\\/]02[\\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)";

@Test
    public void testCheckValidateDate() {
        Assert.assertFalse("Wrong date, january do not have 32 days.", "32/01/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertFalse("Wrong date, February do not have 30 days.", "30/02/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertFalse("Wrong date, 2017 is not Leap year so 28 days", "29/02/2017".matches(DATE_FORMAT_REG_EXP));
        Assert.assertFalse("Wrong date, november have 30 days.", "31/11/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertFalse("Wrong date, 16 instead of 2016.", "30/11/16".matches(DATE_FORMAT_REG_EXP));

        Assert.assertTrue("Good date.", "31/01/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertTrue("Good date, 2016 is Leap year so 29 days", "29/02/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertTrue("Good date.", "28/02/2016".matches(DATE_FORMAT_REG_EXP));
        Assert.assertTrue("Good date.", "28/02/2017".matches(DATE_FORMAT_REG_EXP));
        Assert.assertTrue("Good date.", "30/11/2016".matches(DATE_FORMAT_REG_EXP));
    }

Démo:

Regexp - java - javascript - php - python - C# - ruby - rust - goland - perl

Upvotes: 1

korolar
korolar

Reputation: 1535

There are in fact numerous things wrong with this code. Here are the ones that I've been able to spot:

  1. You're using matcher.group before matcher.matches or matches.find. This is illegal and throws IllegalStateException (see JavaDoc for this method here).
  2. (19|20) is also a matching group, so group 2 will not correspond to the month, as you seem to assume, but to first two digits of the year.
  3. Your algorithm for checking leap years is too simple. Consider:

    @Test
    public void leapYearTricky() {
        Assert.assertFalse(new RegexValidator().validDate("1900-02-29"));
    }
    

Also, not an error per se, but what's the point of matcher.matches, matcher.reset, matcher.find? Why isn't matcher.matches() enough?

Upvotes: 1

ajb
ajb

Reputation: 31699

If m_datePattern is a Pattern, you're using compile incorrectly. The signature of compile is

public static Pattern compile(String regex)

Since it's static, it does not apply to an instance; it's normally called like

Pattern.compile(regex)

Using a Pattern object instead of the class name makes no difference. Thus, if p is a Pattern object, then:

p.compile(regex)

does the exact same thing as Pattern.compile(regex), even if p is null. The object is ignored.

Finally, compile returns a Pattern, which means the result has to be assigned to a Pattern object:

pat = Pattern.compile(Regex);

Your code doesn't assign it anywhere. So the resulting pattern is just thrown away.

Finally, when you use group(n), the groups, starting at 1, are the capture groups beginning with the first (, second (, etc., in the regex. Your regex is

"^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"

In this regex, group(1) is the year; group(2) is 19 or 20; group(3) is the month, and group(4) is the day. Your code is using the wrong groups for the month and day. Either change the numbers, or designate (19|20) as a non-capture group like this:

"^((?:19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"

Now it won't be counted as one of the capture groups.

There may be other errors in your code. These are just the ones I noticed.

Upvotes: 1

Related Questions