With Java 8, the code below parses "18" into year "0018" instead of "2018".
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/y");
return LocalDate.parse(date, formatter);
input date is "01/05/18".
1) why the result is "0018"? Does DateTimeFormatter
not follow the 80-20 rule?
2) How to control SimpleDateFormat parse to 19xx or 20xx? talked about SimpleDateFormat.set2DigitYearStart(Date)
can be used to fix the year. Is there something similar to that for DateTimeFormatter
?
I was hoping "M/d/y" will parse both 2 and 4 digit years.
"M/d/yy" throws Exception for 4 digit years and parses "01/05/97" to "2097-01-05". Ideally this should be parsed to "1997-01-05".
"M/d/yyyy" throws Exception for 2 digit years.
There is not a single string of y
or u
that will allow you to parse both two and four digit years. However, you may use optional parts in the format pattern string to specify that a two or four digit year may be present:
public static LocalDate parseDateString(CharSequence date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/[uuuu][uu]");
return LocalDate.parse(date, formatter);
}
Try it:
System.out.println(parseDateString("01/05/18"));
System.out.println(parseDateString("01/06/2018"));
This printed:
2018-01-05
2018-01-06
In the format pattern string you need to put the four digit year first. With the opposite order, when trying to parse a four digit year, the formatter will parse two digits, decide it was successful this far, and then complain about unparsed text after the two digits.
If you want more precise control over how two digit years are interpreted:
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("M/d/")
.optionalStart()
.appendPattern("uuuu")
.optionalEnd()
.optionalStart()
.appendValueReduced(ChronoField.YEAR, 2, 2, 1920)
.optionalEnd()
.toFormatter();
Using this formatter in the above method let’s try:
System.out.println(parseDateString("01/05/22"));
This prints:
1922-01-05
Giving 1920 as base (as in my example code) will cause two digit years to end up in the interval from 1920 through 2019. Adjust the value to your requirements.
- why the result is "0018"? Does DateTimeFormatter not follow the 80-20 rule?
No, java.time knows no 80-20 rule. You were right, the long obsolete SimpleDateFormat
class used such a rule. Meaning that you could parse and get one result, then create a new formatter the same way you created the first one and get a different result. Because the anchor for the 80-20 rule was different. I find that confusing and error-prone, and you could have had a very hard time debugging such a situation.
Instead, if you specify exactly two digits using uu
, yy
or YY
, java.time consistently implies 20xx: two-digit year 00 is parsed to 2000, 99 to 2099. It will most often not be what you want in all edge cases, but at least it’s deterministic and easy to debug. Depending on your requirements you may supplement it with a range check to make sure that the date is, say, not more than 10 years into the past or the future.
You specified just one letter y
. With y
java.time interpreted the year literally: 18 means year 18, and 2018 means year 2018.