I’m trying to parse a simple string in the format “YYYYww” (e.g. 201901) into a LocalDate, but none of my attempts succeed.
I attempted to parse it by simply using the pattern “YYYYww” and also through manually appending the values to the FormatterBuilder. Since my input string does not contain a day, I also configured the formatter to default to Sunday.
Here’s the code that fails for me, running Java 8 (IBM JRE 8.0.5.25).
public static void main(String[] args) { formatter1(); // Unable to obtain LocalDate from TemporalAccessor formatter2(); // Text '201901' could not be parsed at index 0 formatter3(); // Text '201901' could not be parsed at index 6 } public static void formatter1() { DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendValue(WeekFields.ISO.weekBasedYear(), 4, 4, SignStyle.NEVER) .appendValue(WeekFields.ISO.weekOfYear(), 2, 2, SignStyle.NEVER) .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue()) .toFormatter(); LocalDate.parse("201901", formatter); } public static void formatter2() { DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("YYYYww") .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue()) .toFormatter(); LocalDate.parse("201901", formatter); } public static void formatter3() { DateTimeFormatter formatter = new DateTimeFormatterBuilder() .parseLenient() .appendPattern("YYYYww") .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue()) .toFormatter(); LocalDate.parse("201901", formatter); }
As seen in the example code I get different error messages, with especially the first example confusing me, since the TemporalAccessor contains the week-based-year, the week of the year and the week-day, which should be enough to construct a LocalDate.
Exception in thread "main" java.time.format.DateTimeParseException: Text '201901' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1931) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1866) at java.time.LocalDate.parse(LocalDate.java:411) at Main.formatter1(Main.java:22) at Main.main(Main.java:10) Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed at java.time.LocalDate.from(LocalDate.java:379) at java.time.LocalDate$$Lambda$7.000000001061ED20.queryFrom(Unknown Source) at java.time.format.Parsed.query(Parsed.java:237) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1862) ... 3 more
Advertisement
Answer
This works on Java 8 and later:
DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendValue(WeekFields.ISO.weekBasedYear(), 4) .appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2) .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue()) .toFormatter(); System.out.println(LocalDate.parse("201901", formatter));
Output is:
2019-01-06
I have tested on Oracle Java 1.8.0_101. I haven’t got any IBM Java installed.
The key to making it work is using weekOfWeekBasedYear()
instead of weekOfYear()
. My other changes are cosmetic. WeekFields.ISO.weekOfYear()
defines week numbers by counting the Mondays in the year, which is not what you wanted since week 1 according to ISO may start on a Monday near the end of the previous year (December 29 through 31). Therefore it also doesn’t work well with week-based year, and Java refuses to use the two together to identify a week.
On Java 9 both of your formatter2()
and formatter3()
work too (tested on Oracle Java 9.0.4). It seems there has been a bug in Java 8 where adjacent field parsing didn’t work for YYYYww
. I have searched the Oracle Java Bug Database without finding the bug description, though.
ISO 8601
ISO 8601, the standard which lays down the definition of weeks that you are using, also defines a format for a week. It goes like 2012-W48
. So unless you have a good and very specific reason not to, this is the format that you should use, especially in your exchange of data with any external system. It also happens to eliminate the adjacent fields parsing problem on Java 8.
DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("YYYY-'W'ww") .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue()) .toFormatter(); System.out.println(LocalDate.parse("2019-W01", formatter));
2019-01-06