Sounds simple. There is SimpleDateFormat you may say, or GenericTypeValidator. What's the problem then? Well folks, both libraries are not good enough to parse/format dates.
So we want to allowed users to input dates only in a format "yyyy-MM-dd" or "yyy-MM-dd HH:mm".
Let’s write a unit test for that:
import java.util.Date;
import junit.framework.TestCase;
public class DateTimeFormatTest extends TestCase{
public void testParseDate() throws Exception {
Date date = DateTimeFormat.parseDate("2005-01-01", DateTimeFormat.getDateFormat());
assertNotNull(date);
}
}
OK, now let's write a class:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.validator.GenericTypeValidator;
public class DateTimeFormat {
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
private static final String DATE_FORMAT = "yyyy-MM-dd";
public static Date parseDate(String dateValue, String format) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
Date aParsedDate = null;
try {
aParsedDate = simpleDateFormat.parse(dateValue);
} catch (ParseException e) {
return null;
}
return aParsedDate;
}
public static String getDateFormat() {
return DATE_FORMAT;
}
public static String getDateTimeFormat() {
return DATE_TIME_FORMAT;
}
}
So now if we run a test it succeeds. Let's add additional tests.
date = DateTimeFormat.parseDate("2005-01-aa", DateTimeFormat.getDateFormat());
assertNull(date);
date = DateTimeFormat.parseDate("2005/01/01", DateTimeFormat.getDateFormat());
assertNull(date);
Hey, it's working again. But wait, what about this one:
date = DateTimeFormat.parseDate("2005-01-01 12:12", DateTimeFormat.getDateFormat());
assertNull(date);
Ups, tests don’t pass. Now there is a way that we can tell SimpleDateFormat to be more strict, just add this line
simpleDateFormat.setLenient(false);
unfortunately it won't work. I guess using this kind of parsing is not a good idea :>
But now let's use GenericTypeValidator. We write a test:
public void testValidatorParseDate() throws Exception {
Date date = DateTimeFormat.validatorParseDate("2005-01-01", DateTimeFormat.getDateFormat());
assertNotNull(date);
date = DateTimeFormat.validatorParseDate("2005-01-aa", DateTimeFormat.getDateFormat());
assertNull(date);
date = DateTimeFormat.validatorParseDate("2005/01/01", DateTimeFormat.getDateFormat());
assertNull(date);
date = DateTimeFormat.validatorParseDate("2005-01-01 12:12", DateTimeFormat.getDateFormat());
assertNull(date);
}
and we add a method to the class:
public static Date validatorParseDate(String dateValue, String format) {
Date aParsedDate = GenericTypeValidator.formatDate(dateValue, format, true);
return aParsedDate;
}
Hey this one works. Now add additional test case:
date = DateTimeFormat.validatorParseDate("2005-1-1-1", DateTimeFormat.getDateFormat());
assertNull(date);
Ups again failure. This same thing happens to the next test:
date = DateTimeFormat.validatorParseDate("2005-11-11 1:1:1", DateTimeFormat.getDateTimeFormat());
assertNull(date);
Again using this parser is not a good idea, but it's better then SimpleDateFormat. What to do then? I use regexps, but by using them I force myself to write additional regexp for every date pattern that I allow. In showed example I've only used two date patterns "yyyy-MM-dd HH:mm" and "yyyy-MM-dd" but I used to deal with systems with more then 8 date patterns and it gets pretty messy their, witch means that I have to use some more complicated techniques in order to create a good looking code. Now after refactoring, both classes looks as follow:
import java.util.Date;
import junit.framework.TestCase;
public class DateTimeFormatTest extends TestCase{
public void testValidatorParse() throws Exception {
String dateFormat = DateTimeFormat.getDateFormat();
String dateFormatRegexp = DateTimeFormat.getDateFormatRegexp();
Date date = DateTimeFormat.validatorParse("2005-01-01", dateFormat, dateFormatRegexp);
assertNotNull(date);
date = DateTimeFormat.validatorParse("2005-01-1", dateFormat, dateFormatRegexp);
assertNull(date);
date = DateTimeFormat.validatorParse("2005-01-01", DateTimeFormat.getDateTimeFormat(), dateFormatRegexp);
assertNull(date);
date = DateTimeFormat.validatorParse("2005-01-01", dateFormat, DateTimeFormat.getDateTimeFormatRegexp());
assertNull(date);
date = DateTimeFormat.validatorParse("2005-01-01 12:12", DateTimeFormat.getDateTimeFormat(), DateTimeFormat.getDateTimeFormatRegexp());
assertNotNull(date);
}
public void testParse() throws Exception {
String dateFormat = DateTimeFormat.getDateFormat();
String dateFormatRegexp = DateTimeFormat.getDateFormatRegexp();
Date date = DateTimeFormat.parse("2005-01-01", dateFormat, dateFormatRegexp);
assertNotNull(date);
date = DateTimeFormat.parse("2005-01-1", dateFormat, dateFormatRegexp);
assertNull(date);
date = DateTimeFormat.parse("2005-01-01", DateTimeFormat.getDateTimeFormat(), dateFormatRegexp);
assertNull(date);
date = DateTimeFormat.parse("2005-01-01", dateFormat, DateTimeFormat.getDateTimeFormatRegexp());
assertNull(date);
date = DateTimeFormat.parse("2005-01-01 12:12", DateTimeFormat.getDateTimeFormat(), DateTimeFormat.getDateTimeFormatRegexp());
assertNotNull(date);
}
public void testParseDate() throws Exception {
Date date = DateTimeFormat.parseDate("2005-01-01");
assertNotNull(date);
date = DateTimeFormat.parseDate("2005-01-aa");
assertNull(date);
date = DateTimeFormat.parseDate("2005/01/01");
assertNull(date);
date = DateTimeFormat.parseDate("2005-01-01 12:12");
assertNull(date);
date = DateTimeFormat.parseDate("2005-01-01 12:12");
assertNull(date);
date = DateTimeFormat.parseDate("2005-1-1-1");
assertNull(date);
}
public void testParseDateTime() throws Exception {
Date date = DateTimeFormat.parseDateTime("2005-01-01 11:11");
assertNotNull(date);
date = DateTimeFormat.parseDateTime("2005-11-11 1:1:1");
assertNull(date);
date = DateTimeFormat.parseDateTime("2005-11-11 9:11");
assertNull(date);
date = DateTimeFormat.parseDateTime("2005-11-11");
assertNull(date);
}
public void testValidatorParseDate() throws Exception {
Date date = DateTimeFormat.parseDate("2005-01-01");
assertNotNull(date);
date = DateTimeFormat.validatorParseDate("2005-01-aa");
assertNull(date);
date = DateTimeFormat.validatorParseDate("2005/01/01");
assertNull(date);
date = DateTimeFormat.validatorParseDate("2005-01-01 12:12");
assertNull(date);
date = DateTimeFormat.validatorParseDate("2005-1-1-1");
assertNull(date);
date = DateTimeFormat.validatorParseDateTime("2005-11-11 1:1:1");
assertNull(date);
date = DateTimeFormat.validatorParseDateTime("2005-11-11 11:11");
assertNotNull(date);
}
public void testValidatorParseDateTime() throws Exception {
Date date = DateTimeFormat.validatorParseDateTime("2005-01-01 11:11");
assertNotNull(date);
date = DateTimeFormat.validatorParseDateTime("2005-11-11 1:1:1");
assertNull(date);
date = DateTimeFormat.validatorParseDateTime("2005-11-11 9:11");
assertNull(date);
date = DateTimeFormat.validatorParseDateTime("2005-11-11");
assertNull(date);
}
}
And the main class
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.validator.GenericTypeValidator;
public class DateTimeFormat {
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_FORMAT_REGEXP = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$";
private static final String DATE_TIME_FORMAT_REGEXP = "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$";
/**
* @param dateValue
* @param format
* @param regexp
* @return parsed date if dateValue matches format and regexp, else null
*/
public static Date validatorParse(String dateValue, String format, String regexp) {
Date parsedDate = GenericTypeValidator.formatDate(dateValue, format, true);
return matchRegexp(dateValue, regexp, parsedDate);
}
/**
* @param dateValue
* @param regexp
* @param parsedDate
* @return parsedDate if dateValue matches regexp, else null
*/
private static Date matchRegexp(String dateValue, String regexp, Date parsedDate) {
if(!dateValue.matches(regexp)){
return null;
}
return parsedDate;
}
/**
* @param dateValue
* @return date parsed by SimpleDateFormat
*/
public static Date parseDate(String dateValue) {
return parse(dateValue, getDateFormat(), getDateFormatRegexp());
}
/**
* @param dateTimeValue
* @return date parsed by SimpleDateFormat
*/
public static Date parseDateTime(String dateTimeValue) {
return parse(dateTimeValue, getDateTimeFormat(), getDateTimeFormatRegexp());
}
/**
* @param dateTimeValue
* @return date parsed by GenericTypeValidator
*/
public static Date validatorParseDateTime(String dateTimeValue) {
return validatorParse(dateTimeValue, getDateTimeFormat(), getDateTimeFormatRegexp());
}
/**
* @param dateValue
* @return date parsed by GenericTypeValidator
*/
public static Date validatorParseDate(String dateValue) {
return validatorParse(dateValue, getDateFormat(), getDateFormatRegexp());
}
/**
* @param dateValue
* @param format
* @param regexp
* @return parsed date if dateValue matches format and regexp, else null
*/
public static Date parse(String dateValue, String format, String regexp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
simpleDateFormat.setLenient(false);
Date parsedDate = null;
try {
parsedDate = simpleDateFormat.parse(dateValue);
} catch (ParseException e) {
return null;
}
return matchRegexp(dateValue, regexp, parsedDate);
}
public static String getDateFormat() {
return DATE_FORMAT;
}
public static String getDateTimeFormat() {
return DATE_TIME_FORMAT;
}
public static String getDateFormatRegexp() {
return DATE_FORMAT_REGEXP;
}
public static String getDateTimeFormatRegexp() {
return DATE_TIME_FORMAT_REGEXP;
}
}
Because both SimpleDateFormat and GenericTypeValidator need to be enhanced in order to work properly I'll stick with SimpleDateFormat, mainly because I don't have to use huge apache library in order to parse anything. Two things bother me. Why do I have to write my own validator, and why there is no way in java to remove this redundancy that I see in tests. I'll live it for the future investigation.
No comments:
Post a Comment