I have an entity which has java.time.ZonedDateTime
property. I want to parse a string submitted from form in dd/MM/yyyy HH:mm
format to java.time.ZonedDateTime
.
I tried @DateTimeFormat(pattern = "dd/MM/yyyy HH:mm")
. But it works only LocalDate
with @DateTimeFormat(pattern = "dd/MM/yyyy")
format.
I also created a Converter
as below
public class ZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
private final ZoneId zoneId;
public ZonedDateTimeConverter(ZoneId zoneId) {
this.zoneId = zoneId;
}
@Override
public ZonedDateTime convert(String source) {
LOG.info("Parsing string {} to ZonedDateTime: {}", source, ZonedDateTime.parse(source, DATE_TIME_FORMATTER));
return ZonedDateTime.parse(source, DATE_TIME_FORMATTER);
}
}
and overwriting webMvcConfiguration as below
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ZonedDateTimeConverter(ZoneId.systemDefault()));
}
}
I'm not sure what is the right way to make it work. Using Spring Boot 1.5.6. Thymeleaf 3.0.7
Your DateTimeFormatter
's pattern has only the date (dd/MM/yyyy
) and time (HH:mm
), but a ZonedDateTime
also needs a timezone to be parsed directly.
As the input doesn't have it, one alternative is to first parse the input to a java.time.LocalDateTime
and then convert it to a ZoneDateTime
, using the zoneId
that you already have:
public ZonedDateTime convert(String source) {
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(source, DATE_TIME_FORMATTER);
// convert to ZonedDateTime
return dt.atZone(this.zoneId);
}
Another alternative is to set the timezone in the formatter, so you can parse directly to a ZonedDateTime
. But in this case, I also suggest you a refactor: make the formatter a field of your converter class (instead of a static
field), because it'll depend on the ZoneId
you pass in the constructor.
public class ZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
private final DateTimeFormatter formatter;
public ZonedDateTimeConverter(ZoneId zoneId) {
// set the zone in the formatter
this.formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm").withZone(zoneId);
}
@Override
public ZonedDateTime convert(String source) {
// now the formatter has a zone set, so I can parse directly to ZonedDateTime
return ZonedDateTime.parse(source, this.formatter);
}
}
Just a note about using ZoneId.systemDefault()
. This method gets the JVM's default timezone, but keep in mind that it can be changed without notice, even at runtime, so it's better to always specify which timezone you want to use.
The API uses IANA timezones names (always in the format Continent/City
, like Asia/Kolkata
or Europe/Berlin
). Then you create the timezone using of
method, such as ZoneId.of("Asia/Kolkata")
.
Avoid using the 3-letter abbreviations (like IST
or PST
) because they are ambiguous and not standard. Some of them might work (usually due to retro-compatibility reasons), but it's not guaranteed.
You can get a list of all available timezones (and choose the best for your case) with ZoneId.getAvailableZoneIds()
.