I have encountered a very weird behavior while using SimpleDateFormat
for parsing a string to a date. Consider the following unit test:
@Test
public void testParse() throws ParseException
{
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String dateStr = "2012-12-21";
Date parsedDate = dateFormat.parse(dateStr);
Calendar date = Calendar.getInstance();
date.setTime(parsedDate);
Assert.assertEquals(2012, date.get(Calendar.YEAR));
Assert.assertEquals(11, date.get(Calendar.MONTH)); // yeah, Calendar sucks
Assert.assertEquals(21, date.get(Calendar.DAY_OF_MONTH));
}
As it can be seen there is an intentional mistake in the above code: the SimpleDateFormat
is initialized with "yyyyMMdd"
but the string to be parsed is in the format "yyyy-MM-dd"
. I would expect that such thing results in a ParseException
, or at least be parsed on a best-effort basis correctly. Instead, for some weird reason the date is parsed as 2011-11-02
. Eh?!
This is unacceptable as one single mistake while handling the inputs would result in something totally unexpected / devastating. Switched to JodaTime in the meantime, but it would be nice to understand what went wrong there.
Extract from JavaDoc for setLenient
:
public void setLenient(boolean lenient)
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.
If you set it to false, you will get ParseException
If you use the DateFormat.parse()
function, the string must satisfy the input format. if it doesn't do it, the parse function parse wrong. Here a comment about this in the javaDoc:
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).
Then, your problem would be fixed adding the setLenient(false)
line. In this case, Java throws the exception.
Well, the input would be split into 3 components: year, month, day and you'd get month = -12 and day = -21 (for correction see below). Try to parse 2012/12/21
and you'll get the exception :)
Edit: excerpt from the JavaDoc:
Month: If the number of pattern letters is 3 or more, the month is interpreted as text; otherwise, it is interpreted as a number.
Edit2: Correction
Looking at the source of SimpleDateFormat
it seems that 2012-12-21
is actually split into this:
year = "2012"
month = "-1"
day = "2-"
The source comments state that a -
following a number might either denote a negative number (depending on the locale) or be a delimiter. In your case it seems to be taken as a delimiter, thus day = "2-"
results in day = 2
, hence the second of November.
You need to call setLenient(false). The default is true and Java tries to convert the string even it does not match 100%.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With