Wim Deblauwe
Wim Deblauwe

Reputation: 26858

How to have Spring Boot parse properties bound to LocalTime in ISO8601 format?

I have a Spring Boot 2.2.6 application that uses an @ConfigurationProperties component, something like this:

@Component
@ConfigurationProperties("myapplication")
public class MyApplicationSettings {
    private LocalTime startOfDay;

    // getter and setters here
}

My integration tests suddenly started failing on Jenkins, but worked fine locally. The exception was showing something like this:

Description:

Failed to bind properties under 'myapplication.start-of-day' to java.time.LocalTime:

    Property: myapplication.start-of-day
    Value: 06:00
    Origin: class path resource [application-integration-test.properties]:29:62
    Reason: failed to convert java.lang.String to java.time.LocalTime

Action:

Update your application's configuration

The full exception trace had a root cause of:

Caused by: java.time.format.DateTimeParseException: Text '06:00' could not be parsed at index 5
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalTime.parse(LocalTime.java:463)
    at org.springframework.format.datetime.standard.TemporalAccessorParser.parse(TemporalAccessorParser.java:72)
    at org.springframework.format.datetime.standard.TemporalAccessorParser.parse(TemporalAccessorParser.java:46)
    at org.springframework.format.support.FormattingConversionService$ParserConverter.convert(FormattingConversionService.java:217)

After some digging through the code, I discovered that Spring will use the default locale to parse the LocalTime. As my local machine uses 24-hour notation, it works. The build server is on 12-hour locale, so it fails to parse the string because there is no am/pm indication.

My question:

How to configure Spring Boot to parse the configuration properties as ISO-8601 format, independent from the default locale of the JVM?

I also checked the documentation but the "Properties conversion" chapter does not mention LocalTime (or LocalDate)

Upvotes: 7

Views: 3289

Answers (2)

Mark Bramnik
Mark Bramnik

Reputation: 42491

The solution should consist of the following steps:

  1. Create @ConfigurationProperties annotated class with LocalTime field (MyApplicationSettings in your case) and register it in @Configuration with @EnableConfigurationProperties(MyApplicationSettings.class)
  2. In application.properties define the value:
myapplication.startOfDay=06:00:00
  1. Create a special converter and make sure its scanned and loaded by Spring Boot application. The logic of actual parsing can be different of course, implement whatever you want there, any specific format, locale - whatever:
@Component
@ConfigurationPropertiesBinding
public class LocalTimeConverter implements Converter<String, LocalTime> {
    @Override
    public LocalTime convert(String source) {
        if(source==null){
            return null;
        }
        return LocalTime.parse(source, DateTimeFormatter.ofPattern("HH:mm:ss"));
    }
}
  1. Now all the LocalTime entries in @ConfigurationProperties class will pass through this converter.

Upvotes: 9

sonus21
sonus21

Reputation: 5388

There's a way to do it, but not a direct solution. You need a create a custom ConversionService bean, place this code snippet in the Application file and it should work.

Here I'm registering a new converter that will convert always LocalTime in the ISO_LOCAL_TIME format. You can choose to use other date formatter as well.

  @Bean
  public ConversionService conversionService() {
    ApplicationConversionService conversionService = new ApplicationConversionService();
    conversionService.addConverter(
        String.class,
        LocalTime.class,
        (Converter) source -> LocalTime.parse((String) source, DateTimeFormatter.ISO_LOCAL_TIME));
    return conversionService;
  }

Upvotes: 0

Related Questions