Spring Boot and the LocalDate request parameter

Vaughn Vadachalam
The Startup
Published in
4 min readJun 1, 2020

There is a lot that Spring Boot does exceptionally well, with most of the heavy lifting taken care of for you. It’s all fun and games until something doesn’t work the way you expect it to.

In my case that something is — a REST API with a LocalDate Request Parameter.

To introduce the topic, the code snippet below is Spring Boot’s “Hello World”. The steps to setup and run the application are listed in the Spring Quickstart Guide. In the code below, the hello method has been modified to require one additional parameter named “date” with type LocalDate. The request URL must contain the date parameter and is constructed as /hello?date={X}

Hello LocalDate

Sounds good — What is the supported date format ?

I guessed YYYY-MM-DD (ISO 8601 - it seems legit). I was wrong. Attempts to invoke the endpoint with the URL formatted as /hello?date=2020–01–01, /hello?date=20200101 are met with a consistent error:

Failed to convert value of type ‘java.lang.String’ to required type ‘java.time.LocalDate’

Perhaps a different date format is expected ? Nope, none seem to yield a successful result. Weird, guess a search of the exception message is in order. Great, that resulted in multiple hits, each explaining the methods that can be employed to achieve the desired result.

Some solutions are straightforward, others a bit long-winded, I guess the choice would depend on the situation at hand. The search results did not answer the original question — What is the supported date format ? I did eventually find the answer after extensive debugging — the details of which are listed in Solution 3 below.

The sections below will present three solutions:

  1. Solution 1 — a straightforward solution for most use cases
  2. Solution 2 — a generic solution for a broader range of use cases
  3. Solution 3 — use the supported date format

Solution 1

Add the @DateTimeFormat annotation. It is a concise one-liner with consistent/predictable behavior. The drawback is remembering to add the annotation, everywhere that the data type is used as a parameter. In addition, it is not flexible — a single format must be specified, and only values that conform to said format are accepted.

Hello @DateTimeFormat

The code & comments below are extracted from DateTimeFormat.java:

/**
* Common ISO date time format patterns.
*/
enum ISO {

/**
* The most common ISO Date Format {
@code yyyy-MM-dd},
* e.g. "2000-10-31".
*/
DATE
,
...
...

A (valid) date formatted as described above will be accepted, and produces the expected result, example:

Request 
/hello?date=2020-05-20
Response
Hello World! 2020-05-20

Solution 2

The code snippet below is an implementation of a Spring Type Converter. The Converter interface requires that an implementation specify two type parameters, the first being the type you are converting from, the second being the type you are converting to. In this case it converts a String to a LocalDate.

The mechanics of how conversion is performed is open to the implementer. This affords a bit of flexibility, to illustrate this, the implementation below accepts and converts multiple date formats, both DMY & YMD formats. A brief explanation of the significant sections of code follows below.

Hello LocalDateConverter
  • Line 14 — @Component will automagically inject this Converter into Spring’s internal Conversion service.
  • Line 17 — Configures the list of supported date formats. I would suggest to support at most two formats, ISO 8601 and one other.
  • Lines 18–21 — Constructs a DateTimeFormatter for each of the supported date formats.
  • Lines 26–32 — Parse a String date using the available DateTimeFormatter’s. If a particular parse fails, the unchecked DateTimeParseException is thrown. This exception is ignored to allow all parsers to execute.
  • Line 34 — If the String could not be parsed, then a DateTimeException is thrown, the message includes the source String as well as a list of the supported date formats.

The advantage of this approach is that it provides an application wide, consistent handling of a LocalDate request parameter. Additional annotations are not required. The solution is flexible, and allows for customization of the converter implementation, which can be tailored to suit the application requirements.

Solution 3

Back to the original question — What is the supported date format ?

With a fair amount of debugging, I tracked down the origin of the supported format to the DateTimeFormatterBuilder class.

To inspect the implementation, download the source(s) for the JDK, and locate to the source file (i.e. DateTimeFormatterBuilder.java).

From within an IDE, open the source file, navigate to the formatter(Locale locale, Chronology chrono) method and set a breakpoint on the return statement.

Revert any code changes back to the original sample code (i.e. “Hello LocalDate” listing). Invoke the endpoint with a request that contains the date parameter in any format. The breakpoint should be hit. The values for the key and formatter can then be observed. In my particular case they are:

  • key = ISO|en_US|SHORTnull
  • formatter = Value(MonthOfYear)’/’Value(DayOfMonth)’/’ReducedValue(YearOfEra,2,2,2000–01–01)

Which, when decoded, implies that the supported date format is MM/DD/YY.

An example conversion from an ISO 8601 formatted date of 2020–05–20 to the supported format is 05/20/20.

And sure enough when I tried /hello?date=05/20/20 the response is the magical “Hello World! 2020–05–20”.

Final Thoughts

Contrary to popular online opinion, there is a supported date format, it does however require a bit of effort to find out what it is. Once you figure it out, neither annotations nor type converters are required — a date parameter must conform to the supported format. That said, I tend to view Solution 3 more as a learning experience rather than the actual solution.

The reality is that although ISO 8601 aims to define the standard, there are many variations still very much in use across the globe. It may not be the overriding factor, but when selecting an approach, this fact should be taken into consideration.

--

--