Thursday, June 7th, 2007...10:47 am

Java Date Validation is Annoying (Regexes too)

Jump to Comments

*EDIT* Thanks for the tips in the comments.

Today I had a user complain that they could enter 2 digit years in their app. Hmm I took a look at the code and I didn’t see how that was possible. Here was my date validation method, more after the jump.

[java]

public static boolean isDate(String str)
{
if (str == null || str.equals(“”))
{
return false;
}

SimpleDateFormat sdf = new SimpleDateFormat(“MM-dd-yyyy”);
sdf.setLenient(false); // force pattern to be enforced
java.sql.Date sqlDate;

try
{
sqlDate = new java.sql.Date(sdf.parse(str).getTime());

}
catch (ParseException e)
{
return false;
}
return true;
}
[/java]

Hmm looks OK to me, the problem was that SimpleDateFormat doesn’t work like I thought it would, turns out 01-01-20 will pass through the MM-dd-yyyy mask! So how do you enforce century matching with SimpleDateFormat? Who knows, I was too pissed off reading through the javadoc. Rather than waste time figuring it out I thought I’d just use a regular expression to make sure the string ends with 4 digits.

[java]

//WRONG DUMMY!
String p = “\\d\\d\\d\\d$”;

if(!str.matches(p))
{
return false;
}

[/java]

Wait that didn’t work either. Now I’m really getting steamed, turns out not only do I not understand the SimpleDateFormat class, I also don’t understand the matches method, turns out it doesn’t do partial pattern matching.

So the correct pattern needs to match everything before the last 4 digits as well. So here’s my final method.

[java]

//this works
public static boolean isDate(String str)
{
if (str == null || str.equals(“”))
{
return false;
}

String p = “.*\\d\\d\\d\\d$”;

if(!str.matches(p))
{
return false;
}

SimpleDateFormat sdf = new SimpleDateFormat(“MM-dd-yyyy”);
sdf.setLenient(false); // force pattern to be enforced
java.sql.Date sqlDate;

try
{
sqlDate = new java.sql.Date(sdf.parse(str).getTime());

}
catch (ParseException e)
{
return false;
}
return true;
}
[/java]

Hooray, regex checks for a 4 digit year then simple date format does the leap year legwork and other shenanigans.

If someone who understands how the hell SimpleDateFormat works wants to enlighten me, by all means do so. This just goes to show you, don’t make assumptions in Java, if you’re not creating 74 objects and catching 812 exceptions then you’re probably doing it wrong!

3 Comments

  • Or you could just use SimpleDateFormat(“MM-dd-yy”) to parse for both 2 and 4 digit years.

    Example. This will always work in the first block.

    [java]
    public static void main(String[] args) {

    String dateIn = “01-01-3020″;

    SimpleDateFormat sdf1 = new SimpleDateFormat(“MM-dd-yy”);
    SimpleDateFormat sdf2 = new SimpleDateFormat(“MM-dd-yyyy”);

    Date myDate = null;

    try {
    myDate = sdf1.parse(dateIn);
    System.out.println(“ONE”);
    } catch (ParseException e) {
    try {
    myDate = sdf2.parse(dateIn);
    System.out.println(“TWO”);
    } catch (ParseException e1) {
    System.out.println(“Date Format Error”);
    }
    }

    System.out.println(myDate);
    }
    [/java]

  • Why does it work? ’cause SimpleDateFormat has the ability to deal with Medieval dates!
    Thus you need to explicitly check for that.

    [java]
    GregorianCalendar cal = new GregorianCalendar();

    try {
    Date date = sdf.parse(str);
    cal.setTime(date);
    int year = cal.get(Calendar.YEAR);
    if (year

    [/java]

  • wrt your regexp example:

    //given String input, String regex:

    input.matches(regex) is a convenience method for:
    Pattern.matches(regex, input)

    which in turn is short for:
    Pattern.compile(regex).matcher(input).matches()

    If you’re looking for an occurrence as opposed to matching against the entire regex, use:
    Pattern.compile(regex).matcher(input).find()

    And of course there’s no reason not to cache the compiled Pattern in a real-world application.

Leave a Reply