Wednesday, November 24, 2010

Validating Date in java

I recently ran into a peculiar problem while working with Dates in java. I need to validate an input date provided in the format (dd-MM-yyyy). This seems a very simple requirement.

Input: String
Output: Boolean indicating whether the provided date is in provided format

private static String DATE_FORMAT = "dd-MM-yyyy";
    public boolean validateDate(String date) {
        try {
            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            df.parse(date);
            return true;
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
    }

The following results were observed.

InputActual OutputExpected Output
01-01-9999truetrue
333-01-9999truefalse

As provided in the API documentation of DateFormat states
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).

Thus the unexpected behavior while parsing the input '333-01-9999'. We now change the code to add
private static String DATE_FORMAT = "dd-MM-yyyy";
    public boolean validateDate(String date) {
        try {
            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            df.setLenient(false);
            df.parse(date);
            return true;
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
    }

The following results were observed.

InputActual OutputExpected Output
01-01-9999truetrue
333-01-9999falsefalse
3a-01-9999falsefalse
02-03-1@@@truefalse
02-03-18truefalse

And boom. The parsing of '02-03-1@@@' and '02-03-18' again provides us with an unexpected result. This seems to be a bug in the validation.

So I searched the Sun bug database. But strangely enough the bug has been rejected. The rationale provided is completely and utterly bull*&(#.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5055568

The comments by the Bug submitter have been ignored.

Suppose the input to the method is supposed to be '01-01-2010' and the user provides '01-01-2o10' then the data will be converted into the date 01-01-0002. This after the seeing the setLenient documentation which states that:-
Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format. With strict parsing, inputs must match this object's format.
So the solution to this problem is to match the string using regular expression before validating with the parse method.
private static String DATE_FORMAT = "dd-MM-yyyy";
    private String REGEX_DATE_FORMAT = "^(0[1-9]|[12][0-9]|3[01])[- ](0[1-9]|1[012])[- ]\\d\\d\\d\\d$";
    public boolean validateDate(String date) {
        try {
            if(!Pattern.matches(REGEX_DATE_FORMAT, date)) {
                return false;
            }

            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            System.out.println(df.parse(date));
            return true;
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
    }

InputActual OutputExpected Output
01-01-9999truetrue
333-01-9999falsefalse
3a-01-9999falsefalse
02-03-1@@@falsefalse
02-03-18falsefalse

So we solve the problem.
Hoping some day java will include a simpler way of validation of dates.