A few weeks ago we learned how we can parse date and time information from a request parameter by using the @DateTimeFormat annotation.

This is a simple and clean solution that works very well if we don’t have “too many” controller methods that use this approach.

The drawback of this solution is that we have to annotate the relevant method parameters with the @DateTimeFormat annotation and configure the used date and time format every time when we want to parse date and time information from a request parameter.

This blog post describes how we can solve that problem by using Spring type converters.

Let’s start by creating the type converters that can convert our request parameters into Java 8 LocalDate and LocalDateTime objects.

Creating the Type Converters

We can create a type converter by implementing the Converter<S, T> interface. When we implement that interface, we have to provide two type parameters that are described in the following:

  • The S type parameter describes type of the source object.
  • The T type parameter describes the type of the target object.

Because we want to support both Java 8 LocalDate and LocalDateTime objects, we have create two type converters. Let’s start by creating a type converter that converts String objects into Java 8 LocalDate objects.

Converting String Objects Into Java 8 LocalDate Objects

We can create a type converter that converts String objects into Java 8 LocalDate objects by following these steps:

  1. Create a LocalDateConverter class.
  2. Implement the Converter<S, T> interface, and set the source type to String and the target type to LocalDate.
  3. Add a private DateTimeFormatter field to the created class.
  4. Add a constructor, which takes the used date format as a method parameter, to the created class and implement it by creating a new DateTimeFormatter object.
  5. Override the convert() method by following these rules:
  • If the source string is null or empty, return null.
  • If the source string is not null or empty, return a new LocalDate object.

The source code of the LocalDateConverter class looks as follows:

 1import org.springframework.core.convert.converter.Converter;
 2
 3import java.time.LocalDate;
 4import java.time.format.DateTimeFormatter;
 5
 6public final class LocalDateConverter implements Converter<String, LocalDate> {
 7
 8    private final DateTimeFormatter formatter;
 9
10    public LocalDateConverter(String dateFormat) {
11        this.formatter = DateTimeFormatter.ofPattern(dateFormat);
12    }
13
14    @Override
15    public LocalDate convert(String source) {
16        if (source == null || source.isEmpty()) {
17            return null;
18        }
19
20        return LocalDate.parse(source, formatter);
21    }
22}

If you are using Spring Framework 4.1 (or newer), you don’t have to ensure that the source string is not null because Spring Framework guarantees that it is never null. However, if you use Spring 4.0 (or older), you might want to check that the source string is not null because the Javadoc of the Converter<S, T> interface doesn’t state that the method parameter of the convert() method cannot be null.

Additional Reading:

Let’s find out how we can create a type converter that converts String objects into Java 8 LocalDateTime objects.

Converting String Objects Into Java 8 LocalDateTime Objects

We can create a type converter that converts String objects into Java 8 LocalDateTime objects by following these steps:

  1. Create a LocalDateTimeConverter class.
  2. Implement the Converter<S, T> interface, and set the source type to String and the target type to LocalDateTime.
  3. Add a private DateTimeFormatter field to the created class.
  4. Add a constructor, which takes the used date and time format as a method parameter, to the created class and implement it by creating a new DateTimeFormatter object.
  5. Override the convert() method by following these rules:
  • If the source string is null or empty, return null.
  • If the source string is not null or empty, return a new LocalDateTime object.

The source code of the LocalDateTimeConverter class looks as follows:

 1import org.springframework.core.convert.converter.Converter;
 2
 3import java.time.LocalDateTime;
 4import java.time.format.DateTimeFormatter;
 5
 6public final class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
 7
 8    private final DateTimeFormatter formatter;
 9
10    public LocalDateTimeConverter(String dateFormat) {
11        this.formatter = DateTimeFormatter.ofPattern(dateFormat);
12    }
13
14    @Override
15    public LocalDateTime convert(String source) {
16        if (source == null || source.isEmpty()) {
17            return null;
18        }
19
20        return LocalDateTime.parse(source, formatter);
21    }
22}

If you are using Spring Framework 4.1 (or newer), you don’t have to ensure that the source string is not null because Spring Framework guarantees that it is never null. However, if you use Spring 4.0 (or older), you might want to check that the source string is not null because the Javadoc of the Converter<S, T> interface doesn’t state that the method parameter of the convert() method cannot be null.

Additional Reading:

Before we can use our new type converters, we have to configure the application context of our web application.

Configuring the Application Context of Our Web Application

Before we can use our type converters, we have to add them to the Spring formatter registry. This section describes how we can configure the application context of our web application that uses either Spring or Spring Boot.

Configuring a Spring Web Application

If we want to add our type converters into the formatter registry of a Spring web application, we have to make the following changes to the configuration class that configures the web layer of our Spring web application:

  1. Extend the WebMvcConfigurerAdapter class.
  2. Override the addFormatters(FormatterRegistry registry) method of the WebMvcConfigurerAdapter class.
  3. Implement this method by adding our type converters into the Spring formatter registry.

The source code of the WebMvcContext class looks as follows:

 1import org.springframework.context.annotation.Configuration;
 2import org.springframework.format.FormatterRegistry;
 3import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 4import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 5
 6@Configuration
 7@EnableWebMvc
 8class WebMvcContext extends WebMvcConfigurerAdapter {
 9
10	@Override
11	public void addFormatters(FormatterRegistry registry) {
12		registry.addConverter(new LocalDateConverter("yyyy-MM-dd"));
13		registry.addConverter(new LocalDateTimeConverter("yyyy-MM-dd'T'HH:mm:ss.SSS"));
14	}
15}

Let’s move on and find out how we can add our type converters into the formatter registry of a Spring Boot web application.

Configuring a Spring Boot Web Application

If we want to add our type converters into the formatter registry of a Spring Boot web application, we have to make the following changes to the “application class” of our Spring Boot web application:

  1. Extend the WebMvcConfigurerAdapter class.
  2. Override the addFormatters(FormatterRegistry registry) method of the WebMvcConfigurerAdapter class.
  3. Implement this method by adding our type converters into the Spring formatter registry.

The source code of the SpringBootExampleApplication class looks as follows:

 1import org.springframework.boot.SpringApplication;
 2import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 3import org.springframework.context.annotation.ComponentScan;
 4import org.springframework.context.annotation.Configuration;
 5import org.springframework.format.FormatterRegistry;
 6import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 7
 8@Configuration
 9@EnableAutoConfiguration
10@ComponentScan
11public class SpringBootExampleApplication extends WebMvcConfigurerAdapter {
12
13    @Override
14    public void addFormatters(FormatterRegistry registry) {
15        registry.addConverter(new LocalDateConverter("yyyy-MM-dd"));
16        registry.addConverter(new LocalDateTimeConverter("yyyy-MM-dd'T'HH:mm:ss.SSS"));
17    }
18
19    public static void main(String[] args) {
20        SpringApplication.run(SpringBootExampleApplication.class, args);
21    }
22}

We are done. Let’s find out how we can use our type converters.

Using the Type Converters

After we have configured the application context of our web application, we can use our type converters by following these steps:

  1. Add a new method parameter into our controller method and set the type of this method parameter to LocalDate or LocalDateTime.
  2. Configure the name of the request parameter by annotating the method parameter with the @RequestParam annotation.

The source code of our controller class looks as follows:

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.web.bind.annotation.RequestMapping;
 3import org.springframework.web.bind.annotation.RequestMethod;
 4import org.springframework.web.bind.annotation.RequestParam;
 5import org.springframework.web.bind.annotation.RestController;
 6
 7import java.time.LocalDate;
 8import java.time.LocalDateTime;
 9
10@RestController
11@RequestMapping("/api/datetime/")
12final class DateTimeController {
13
14    @RequestMapping(value = "date", method = RequestMethod.POST)
15    public void processDate(@RequestParam("date") LocalDate date) {
16        //Do stuff
17    }
18
19    @RequestMapping(value = "datetime", method = RequestMethod.POST)
20    public void processDateTime(@RequestParam("datetime") LocalDateTime dateAndTime) {
21        //Do stuff
22    }
23}

Let’s move on and summarize what we learned from this blog post.

Summary

This blog post has taught us two things:

  • We can create a new type converter by implementing the Converter<S, T> interface.
  • Before we can use our type converter, we have to add it into the Spring formatter registery. We can do this by extending the WebMvcConfigurerAdapter class and overriding its addFormatters(FormatterRegistry registry) method.

P.S. You can get the example application of this blog post from Github.