Cover image for YYYY vs yyyy - The day the Java Date Formatter hurt my brain

YYYY vs yyyy - The day the Java Date Formatter hurt my brain

shane profile image shane ・2 min read


Dates in programming are hard. The ISO-8601 Date standard made them easier but to be entirely honest I'm not ISO-8601 Certified so I'm not very good at my job. What I do know though is the very important difference between YYYY and yyyy when formatting dates.

The Difference between y and Y

According to the DateTimeFormatter Java Docs (which implements the ISO-8601 specification)

  • y (lowercase) is year
  • Y (uppercase) is 'week-based-year'.

This difference will cause your code to work perfectly fine, except for when dealing with dates at the very end of some years.

Here is an example that I had the absolute PLEASURE of dealing with last year. For the date 31/12/2019 (December 31st 2019), yyyy will output 2019 but YYYY will output 2020. The reason for this is the week that the 31st of December falls in technically is the first week of 2020. This issue can be a pain to notice and debug.
In my case, 31/12/2019 was being sent from the frontend, being stored correctly in the DB, but was turning into 31/12/2020 when being returned and no one had any idea why. Our default JSON date formatter was the culprit, using YYYY where it should have been using yyyy (some clumsily copied and pasted quick solution by myself ofcourse).


Declare formatters

// y (Lowercase) 
DateTimeFormatter formatteryyyy = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// Y (Uppercase)
DateTimeFormatter formatterYYYY = DateTimeFormatter.ofPattern("YYYY-MM-dd");

When converting from Date to String

// Contains value from Database (December 31st 2019) 
LocalDateTime dateTime;

// String value will be 2019-12-31
String yyyy = dateTime.format(formatteryyyy);

// String value will be 2020-12-31
String YYYY = dateTime.format(formatterYYYY);

When convert from String to Date

// Actual date value will be 2020-12-31
LocalDateTime parsedDateyyyy = LocalDateTime.parse("2020-12-31", formatteryyyy); 

// Actual date value will be 2019-12-31
LocalDateTime parsedDateYYYY= LocalDateTime.parse("2020-12-31", formatterYYYY);  


In 99% of use cases, the following is true:

  • yyyy good
  • YYYY bad


Editor guide

Parsing dates with patterns is like parsing emails or HTML. Usually, you shouldn’t do it.

Pre Java 8, I also used those formatters but I regularly ran into bugs like yours or undiscovered edge cases.

Since Java 8, we can simply use Instant::toString and Instant::parse to convert from and to valid ISO time stamps. Replace Instant by OffsetDateTime or ZonedDateTime when you want to preserve offsets or even time zones (which is usually also
not a good idea).


You know you’re going to have to do a follow up on the 1% use case where YYYY is good, right? #cliffhanger


I won't and you can't make me :(