I am facing a issue with an API to handle dates in Java. I am not a very experienced programmer and my English is very bad.
Problem
I'm working in a large project with many subsystems, and I want to make a Date Utility library to manage all dates in the system.
Requirements
Platform
- The front end is a JSF page with
RichFaces
- The application generate reports with
JasperReports
- The back end is a Spring 3.1 Application
- Is desirable to support
Calendar
andXMLGregorianCalendar
(for Jaxb2 Serialization)
Business Logic
- The system must support six types of dates:
- Short date format: dd-MM-yy
- Date format: dd-MM-yyyy
- Time format (only hours and minutes): HH:mm
- Time and date: dd-MM-yyyy HH:mm
- Time and short date: dd-MM-yy HH:mm
- The consistency is the most important thing in the design
The long format is for details, and the short is for grids and places with little space.
API
// Format using dd-MM-yy
public String asShortDate(@Nullable Date date) ;
// Parse using dd-MM-yyy
public Date parseShortDate(@Nullable String string) throws ParseException;
// Format using dd-MM-yyyy
public String asDate(@Nullable Date date);
// Parse using dd-MM-yyyy
public Date parseDate(@Nullable String string) throws ParseException;
// Format using HH:mm
public String asTime(@Nullable Date date);
// Parse using HH:mm
public Date parseTime(@Nullable String string) throws ParseException;
// Format using dd-MM-yyyy HH:mm
public String asDateTime(@Nullable Date date);
// Parse using dd-MM-yyyy HH:mm
public Date parseDateTime(@Nullable String string) throws ParseException;
// Format using dd-MM-yy HH:mm
public String asShortDateTime(@Nullable Date date) ;
// Parse using dd-MM-yy HH:mm
public Date parseShortDateTime(@Nullable String string) throws ParseException;
Parser and Formatter
protected synchronized String dateToString(Date date, String format) {
if (date == null) {
return EMPTY_STRING;
}
return getFormat(format).format(date);
}
/**
* @param string
* @return
* @throws ParseException
*/
protected synchronized Date stringToDate(String string, String format)
throws ParseException {
if (string == null || EMPTY_STRING.equals(string)) {
return null;
}
SimpleDateFormat sdf = getFormat(format);
ParsePosition psp = new ParsePosition(0);
if (string.length() != format.length()) {
throw new ParseException("Imposible parsear", 0);
}
Date toRet = sdf.parse(string, psp);
if (psp.getIndex() != string.length()) {
throw new ParseException("Imposible parsear", psp.getIndex());
}
return toRet;
}
/**
* Provee un formato
*
* @param format
* @return
*/
private SimpleDateFormat getFormat(String format) {
if (!initialized) {
init();
}
if (!formats.containsKey(format)) {
throw new IllegalArgumentException("Unknow format " + format);
}
return formats.get(format);
}
Other
private synchronized void init() {
if (initialized) {
return;
}
initialized = true;
formats = new HashMap<String, SimpleDateFormat>(5);
formats.put(DATE_FORMAT, new SimpleDateFormat(DATE_FORMAT));
formats.put(DATE_SHORT_FORMAT, new SimpleDateFormat(DATE_SHORT_FORMAT));
formats.put(TIME_FORMAT, new SimpleDateFormat(TIME_FORMAT));
formats.put(DATETIME_FORMAT, new SimpleDateFormat(DATETIME_FORMAT));
formats.put(DATETIME_SHORT_FORMAT, new SimpleDateFormat(
DATETIME_SHORT_FORMAT));
for (SimpleDateFormat sdf : formats.values()) {
sdf.setLenient(false);
}
}
Considerations
- I made the methods
stringToDate
,dateToString
and init as synchronized becauseSimpleDateFormat
is not thread-safe, and I only want one initialization. - I made the
DateFormat
not lenient because I don't want to handle wrong format dates - The methods names are for enhance readability (as* always return a String, and parse* always return a Date).
Further develop
- A JSF Date converter to manage automatically all dates.
- A JSF Phase listener to format all dates.
- A @Annotation to differentiate between different kinds of types
My Questions
- Is this approach right and maintainable?
- My considerations are right?
- How I can improve this API?
- Is correct to be restrictive about the lenient property, or is a waste of time?
Source
- Code: https://gist.github.com/aVolpe/6558296
- Current test: https://gist.github.com/aVolpe/6558316
Edit
Observation: this question was answered in stack overflow