Search code examples
javadatetime-formatlocaldate

LocalDate cannot parse 'ww' with 'yyyy'


I have to parse date using the following format: "201710" where 10 - week of year number. I tried to implement it in this way:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyw");
java.time.LocalDate startDate = java.time.LocalDate.parse("201710", formatter);
System.out.println(startDate);

But it throws exception:

java.time.format.DateTimeParseException: Text '201710' could not be parsed at index 0   

And after that I need to get first and last day of week from LocalDate object. e.g "201710" - 05.03 12.03 (first day of week needs to be Sunday).


Solution

  • The accepted answer of @Kayaman is not correct because you cannot mix standard date representations (using yyyy = year-of-era) and week-date representations (using ww = week of week-based year). The subtile difference between a standard calendar year and a weekbased year is relevant near the start or end of a calendar year. Conclusion: Don't use the symbol "y", but rather the symbol "Y". Counter example for the input "201501":

    Correct solution

    DateTimeFormatter formatter =
        new DateTimeFormatterBuilder()
        .appendValue(WeekFields.ISO.weekBasedYear(), 4)
        .appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2)
        .parseDefaulting(ChronoField.DAY_OF_WEEK, 1)
        .toFormatter();
    LocalDate startDate = LocalDate.parse("201501", formatter);
    System.out.println(startDate); // 2014-12-29
    

    Based on the proposal of @Kayaman:

    DateTimeFormatter dtf =
        new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR, 4)
        .appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2)
        .parseDefaulting(WeekFields.ISO.dayOfWeek(), 1)
        .toFormatter();
    System.out.println(LocalDate.parse("201501", dtf)); // 2015-01-05 (wrong)
    

    The resulting dates are different! The difference is caused by the definition of the calendar year which always starts on first of January while a week-based year always starts on Monday (ISO-8601-definition) using the first week of calendar year which has at least 4 days.

    Additional note a): Java-8 does not manage adjacent digit parsing of localizible fields like the week-based fields (see also the associated JDK issue), therefore I have chosen the builder-based solution instead of defining the pattern "YYYYww" (Java-9 promises a solution, however). But even with Java-9, a build-based approach is still necessary because of the need to define a default for the missing day-of-week (here: setting to Monday).

    Additional note b): If you are looking for a true type for the combination of week-based year and week-of-year and use LocalDate just as a workaround for this missing type, well, you can find such a type in 3rd-party libraries, either in Threeten-Extra or in my library Time4J. Example:

        ChronoFormatter<CalendarWeek> cf =
            ChronoFormatter.ofPattern(
                "YYYYww",
                PatternType.CLDR,
                Locale.ROOT,
                CalendarWeek.chronology()
            );
        CalendarWeek cw = cf.parse("201501");
        System.out.println(cw); // 2015-W01
        System.out.println(cw.at(Weekday.MONDAY)); // 2014-12-29