Beware using DateFormat for input validation
For some time now I’ve been using @DateFormat@ (actually @SimpleDateFormat@) to parse user inputted dates it situations where a decent date chooser wasn’t available. Some simple bit of code like this would normally suffice:
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/mm/YYYY");...
try { DATE_FORMAT.parse(someString);
} catch (ParseException pe) { //date is invalid
}
I learnt two things about this approach. Firstly, DateFormat is not thread safe. This fact was only documented in Java 1.4 despite it having never been thread safe. This is one of those things you learn, and is easily put right.
The second thing however, is that the parsing itself is lazy to the point that it is actually just plain wrong. Lets take 12/12/200 as a value for someString in the code above. SimpleDateFormat will parse the string and return a date (12th of December in the year 200 AD) and Will not throw an exception, despite the fact we specified a four digit year. This is certainly not the expected behaviour – what if I missed off a final digit by accident? Even worse, it will also parse 12/12/200T. The nearest the documentation comes to warning us of this behaviour is this little note in the parse method:
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
From this one might of guessed that it would parse 12/12/2004T as a valid date, but not 12/12/200T…
The net result is that we’ve had to change our date validation code over to a two pass system – we use a SimpleDateFormat to make sure we are using sensible values (e.g. not trying to enter the 33rd of June or something) and then use a StringTokenizer to break up the string and check that four digits are being entered for the year (we only have Java 1.3 available to us so no regular expressions). Just another example of why the Calendar API is perhaps the worst on the Java platform.
Update: We managed to find a solution to the problem with the lazy parsing – the DateFormat.setLenient() turns on strict error checking:
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.
Perhaps another case of me not reading the Javadoc, however this method is not described in the overviews of either DateFormat or SimpleDateFormat.
This entry was posted on Monday, May 17th, 2004 at 5:46 pm and is filed under Java. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pinging is currently not allowed.
5 Responses to “Beware using DateFormat for input validation”
Actually, I think its just SimpleDateFormat that is not thread-safe, but don’t quote me. I generally use the Jakarta commons-lang 2.0 FastDateFormat class, thread-safe and quicker than SimpleDateFormat.
What context are you entering dates? There are a number of good javascript date validation scripts around, and some free Java implementations as well.
Edited to stop layout problem:
Javascript Validation Links validation&btnG=Search
According to the documentation,
DateFormatisn’t synchronized either:We discovered the non-threadsafeness of SimpleDateFormat a few months ago. It bit us big time…
We ended up doing this to force it….
/*
* Created on 11-Jun-04
/
package domain;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
/*
* @author Colin Hawkett
*/
public class StrictDateFormat extends SimpleDateFormat {
public StrictDateFormat(String format) {
super(format);
}
public java.util.Date parse(String dateString) throws ParseException {
if(dateString.length() != toPattern.length()) throw new ParseException(“Input string wrong length!”, 0);
ParsePosition pos = new ParsePosition(0);
java.util.Date date = super.parse(dateString, pos);
if((date == null) || (pos.getErrorIndex() != -1)) throw new ParseException(“Dodgy date string!”, pos.getIndex());
if(pos.getIndex() != toPattern().length()) throw new ParseException(“Did not use entire string to parse date!”, pos.getIndex());
else return date;
}
}
One more note: without the dateFormat.setLenient(false), the SimpleDateFormatter will convert a date like 12/44/2004 into something like 1/13/2005. It doesn’t catch months_out_of_range or days_in_month_out_of_range errors.
Have your say
Fields in bold are required. Email addresses are never published or distributed.
Some HTML code is allowed:
URIs must be fully qualified (eg: http://www.domainname.com) and all tags must be properly closed.
Line breaks and paragraphs are automatically converted.
Please keep comments relevant. Off-topic, offensive or inappropriate comments may be edited or removed.