Alex Corrigan


Java's "Week Based Year" DateTimeFormatter

Investigating the true behaviour of the "YYYY" formatter pattern.

I stumbled upon this tweet the other day:

This surprised me a bit and wanted to check it out for myself. I often use the DateTimeFormatter and, not really thinking too much about it, construct them with a pattern of the form YYYY-MM-dd. I always expect it to just format the year as the year that was passed to it, not go of and work out the year by some other means.

Let’s see what actually happens. Here are two uses of the DateTimeFormatter with that pattern I usually use:

1
2
3
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd");
System.out.println(dateTimeFormatter.format(LocalDate.of(2019, 12, 29)));
System.out.println(dateTimeFormatter.format(LocalDate.of(2019, 12, 30)));

The first date formatted results in: 2019-12-29

The second date formatted results in: 2020-12-30

Uh-oh! That could cause all kinds of problems.

It turns out “Y” part of the pattern will format a date’s year as week-based-year. That is the year in which the Thursday of the week falls in. As is the case this very week, beginning 30/12/2019, the Thursday of this week is 02/01/2020. Therfore passing any date for this week into that DateTimeFormatter with result in a formatted date in the year 2020.

This is to some extent documented in the Java docs for DateTimeFormatter, but could be easily missed or overlooked.

What I should be using to achieve my expected behaviour of returning a result with the specific year of the single date passed to the formatter is a pattern with the lowercase “y” eg. “yyyy-MM-dd”:

1
2
3
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println(dateTimeFormatter.format(LocalDate.of(2019, 12, 29)));
System.out.println(dateTimeFormatter.format(LocalDate.of(2019, 12, 30)));

The first date formatted now results in: 2019-12-29

The second date formatted now results in: 2019-12-30

As intended.