Parse A Date With A Variable Format Following Principles
A friend of mine came across this somehow difficult situation where he had to parse a date only that he has no way of knowing the format.
In cases like these we usually apply “brute force” to overcome them as they look rather trivial and can’t be bothered.
However, it’s good if we hold our train of thoughts as this will lead to a code that is easier to extend, reuse, maintain and even understand in days to come.
So what’s wrong with the DateUtils class? Well, a couple of things.
- If you need to support another DateFormat you must re visit the #textToDate method and add an extra try/catch. That might look like a trivial thing to do, but every time you open a class you risk breaking the implementation.
- Having a static method doesn’t allow you to extend through composition or inheritance.
- You can’t reuse instances of the DateUtil class since the method is static.
- It doesn’t extend DateFormat so you have to add new code to accommodate for your DateUtil which is merely a different implementation for parsing dates. ([Disclaimer])
Validates the contract of Format#parse(String)
- The contract of the method #parse(String):Date defines a throw ParseException in case an error occurs during parsing. Your client code using any date format to parse the dates might look like it’s behaving correctly since it’s not catching an expected Exception.
- You are forced to check for null every time you use that way of parsing dates.
As you can see following principles isn’t all that subjective as you might think. So the next time you decide to violate any at least do it for the right reasons. If there are any that is.
Software development shouldn’t be a throwaway process.
Disclaimer Your custom class should extend the DateFormat class. However, DateFormat as an abstract class forces you to also override both
#format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
#parse(String source, ParsePosition pos)
which on its own is a chapter on API design. Ideally an interface should have been present which holds the single method #parse(String):Date which you should implement.
Kudoz Heinz Kabutz who wrote an article on the cost of causing Exceptions. Not performance wise, my personal opinion is that the DateFormat should have a method #isParsable(String) which can be used instead.
Update While writing this post it came to my attention that all of the date formats should have lenient to false as you risk interpreting a date with a wrong format. Another reason why you should test even trivial code!
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).*
Also the order of the DateFormat(s) can play an important role as you may find yourself parsing a date wrong for cases where the day is <=12 and a SimpleDateFormat(“MM-dd-yyyy”) comes before a new SimpleDateFormat(“dd-MM-yyyy”). Though in some cases a different ordering isn’t sufficient.