javadatetimeformatter

DateTimeFormatter gets confusing results for LocalDateTime.withYear(0)?


Java version: 1.8.0_202
see code below:

DateTimeFormatter yy = DateTimeFormatter.ofPattern("yy");
DateTimeFormatter yyy = new DateTimeFormatterBuilder()
                .appendValueReduced(ChronoField.YEAR, 3, 3, 0)
                .toFormatter();
DateTimeFormatter yyyy = DateTimeFormatter.ofPattern("yyyy");

LocalDateTime now = LocalDateTime.now();
// set year is 0
LocalDateTime year0 = now.withYear(0);
System.out.println("year0: " + year0);                             // year0: 0000-01-10T09:53:03.551
System.out.println("year0.getYear(): \t" + year0.getYear());       // year0.getYear():    0
System.out.println("year0.format(yyyy): " + year0.format(yyyy));   // year0.format(yyyy): 0001
System.out.println("year0.format(yyy): \t" + year0.format(yyy));   // year0.format(yyy):  000
System.out.println("year0.format(yy): \t" + year0.format(yy));     // year0.format(yy):   01
System.out.println("============================================================");
// set year is 1
LocalDateTime year1 = now.withYear(1);
System.out.println("year1: " + year1);                             // year1: 0001-01-10T09:53:03.551
System.out.println("year1.getYear(): \t" + year1.getYear());       // year1.getYear():     1
System.out.println("year1.format(yyyy): " + year1.format(yyyy));   // year1.format(yyyy):  0001
System.out.println("year1.format(yyy): \t" + year1.format(yyy));   // year1.format(yyy):   001
System.out.println("year1.format(yy): \t" + year1.format(yy));     // year1.format(yy):    01

Why year0.format(yyyy) and year0.format(yy) return wrong result 0001 or 01, but year0.format(yyy) return right result 000? how to fix?


Solution

  • The docs for DateTimeFormatter say that y is for "year of era", meaning should never output 0 because no era (AD or BC) had a year 0. According to those same docs, you're looking for u instead of y if you want the year displayed without eras, i.e. treating 1 BC as year "0", 2 BC as year "-1", etc.

    If instead you want to display the era along with the year, that's G, i.e. yyyy GG.

    Dates that many centuries in the past can be complicated because of calendar changes between then and now, so be careful about assuming you can just plug them into a standard library and get "correct" results.

    "yyy" probably gave you a different result because "yyy" and "uuu" have special case rules, as described in the docs. Only use those if you want your output formatted with those particular rules.