Reputation: 6962
I have a REST endpoint in my Spring application that looks like this
@RequestMapping(value="/customer/device/startDate/{startDate}/endDate/{endDate}", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public Page<DeviceInfo> getDeviceListForCustomerBetweenDates(@PathVariable ZonedDateTime startDate, @PathVariable ZonedDateTime endDate, Pageable pageable) {
... code here ...
}
I have tried passing in the path variables as both milliseconds and seconds. However, I get the following exception both ways:
"Failed to convert value of type 'java.lang.String' to required type 'java.time.ZonedDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.web.bind.annotation.PathVariable java.time.ZonedDateTime for value '1446361200'; nested exception is java.time.format.DateTimeParseException: Text '1446361200' could not be parsed at index 10"
Can someone please explain how I can pass in (either as seconds or milliseconds) a String such as 1446361200 and get it to convert to ZonedDateTime?
Or is the only way to pass as String and then do the conversion myself? If so is there a generic way to get this handled for multiple methods with similar design?
Upvotes: 15
Views: 14786
Reputation: 280031
There's a default converter for ZonedDateTime
parameters. It uses Java 8's DateTimeFormatter
created like
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
This could've been any FormatStyle
or really any DateTimeFormatter
as far as you're concerned, your example wouldn't have worked. DateTimeFormatter
parses and formats to date strings, not timestamps, which is what you've provided.
You could have provided an appropriate custom @org.springframework.format.annotation.DateTimeFormat
to your parameter such as
public Page<DeviceInfo> getDeviceListForCustomerBetweenDates(
@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate,
@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate,
Pageable pageable) { ...
or with an appropriate pattern
and a corresponding date string like
2000-10-31T01:30:00.000-05:00
You won't be able to do any of the above with a unix timestamp. The canonical way to do timestamp to ZonedDateTime
conversion is through Instant#ofEpochSecond(long)
, given an appropriate ZoneId
.
long startTime = 1446361200L;
ZonedDateTime start = Instant.ofEpochSecond(startTime).atZone(ZoneId.systemDefault());
System.out.println(start);
To make this work with @PathVariable
, register a custom Converter
. Something like
class ZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
private final ZoneId zoneId;
public ZonedDateTimeConverter(ZoneId zoneId) {
this.zoneId = zoneId;
}
@Override
public ZonedDateTime convert(String source) {
long startTime = Long.parseLong(source);
return Instant.ofEpochSecond(startTime).atZone(zoneId);
}
}
And register it in a WebMvcConfigurationSupport
@Configuration
annotated class, overriding addFormatters
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ZonedDateTimeConverter(ZoneId.systemDefault()));
}
Now, Spring MVC will use this converter to deserialize the String
path segment into a ZonedDateTime
object.
In Spring Boot, I think you can just declare a @Bean
for the corresponding Converter
and it will register it automatically, but don't take my word for it.
Upvotes: 18
Reputation: 88796
By default, @PathVariable
only supports a limited set of types including the 7 basic types (boolean
, byte
, short
, int
, long
, float
, and double
) plus Date
.
However, it is possible to extend that using an @InitBinder
in your controller and adding a binding for ZonedDateTime.class
. I'm not sure if you can define this in an external class, though... if not, you need to define this @InitBinder
in every controller that uses a ZonedDateTime
.
Edit: You may need to create a custom PropertyEditor
and bind it using registerCustomEditor
to do this.
Upvotes: 1
Reputation: 4180
You gonna need to accept what you passed to @PathVariable
as a String
data type, then do the conversion yourself, your error logs pretty clearly tells you that.
Spring library cannot conver String values"1446361200" to ZonedDateTime
type through @PathVariable
binding unfortunately.
Upvotes: 5