Spring Boot change date format of request param field as DTO - java

My controller has a GET endpoint which accept lots of query parameters. So, instead of having multiple #QueryParam I created a CriteriaDTO in order to perform dynamic queries to our Mongo database
My controller endpoint :
#GetMapping
public List<MyObject> getAll(#Valid CriteriaDTO criteriaDTO){
return myObjectService.findAll(criteriaDTO);
}
public class CriteriaDTO {
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
// getters, setters ...
}
And, I want to pass the minDate is the URL with the following format yyyy-MM-dd but I need to convert it to the following format yyyy-MM-dd'T'HH:mm:ss.SSS.
My question is : Is there any annotation or something else which accepts the first format yyyy-MM-dd and automatically convert it to another ?
To be clear if I make the following call :
http://localhost:8080/api/myobject?minDate=2020-01-01
And then criteriaDTO.getminDate() will return 2020-01-01'T'00:00:00.000
Thanks for your help :)

You can do it in a more simple way than searching an annotation-magic solution.
Just add to your CriteriaDTO an additional getter for LocalDateTime:
public LocalDateTime getMinDateTime() {
return this.minDate.atTime(0, 0, 0, 0);
}
and use it wherever you need time instead of date.

Define setter and parse with SimpleDateFormat
public void setMinDate() {
if(!minDate.empty()) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
this.minDate = formatter.parse(minDate)
}
}

I would recommend to use atStartOfDay instead of converting this 2020-01-01 to 2020-01-01'T'00:00:00.000 using custom deserializer. And also since you are manipulating the input data i would recommend to do it as separate operation
LocalDateTime date = criteriaDTO.getminDate().atStartOfDay() //2020-01-01'T'00:00
And you can also add formatter DateTimeFormatter to get required output
date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) //2020-01-01'T'00:00:00

You have several options. Check what you exactly need,
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.of(date, LocalTime.MIDNIGHT);
System.out.println(dateTime); //2020-02-04T00:00:00
System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(dateTime)); //2020-02-04T00:00:00
System.out.println(date.atStartOfDay()); ////2020-02-04T00:00
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
System.out.println(formatter.format(dateTime)); //2020-02-04T00:00:00.000
You need to modify the getter in dto to format it, for example:
class CriteriaDTO {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
public String getMinDate() {
return formatter.format(LocalDateTime.of(minDate, LocalTime.MIDNIGHT));
}
// getters, setters ...
}

Related

MessageConversionException occurred when mapping string date to LocalDateTime

I have a queue that listens to a topic and my listener receives a DTO.
I need to parse the String to LocalDateTime but I'm getting this error
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Text '2020-06-18 11:12:46' could not be parsed at index 10
Here is the message details
{"id":444, "details":{"TYPE":[1]},"dateOccurred":"2020-06-18 11:12:46"}"]
And here is how I set it in my DTO
public class PlanSubscriptionDto {
private Long id;
private Map<String, List<Long>> details;
private LocalDateTime dateOccurred;
public void setDateOccurred(String dateTime) {
this.dateOccurred = LocalDateTime.parse(dateTime, DateTimeFormatter.ISO_DATE_TIME);
//ive also tried this
//this.dateOccurred = LocalDateTime.parse(dateTime, DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG));
}
}
Thank you for your help!
Any advice would we great.
Use a format pattern string to define the format.
public class PlanSubscriptionDto {
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
private Long id;
private Map<String, List<Long>> details;
private LocalDateTime dateOccurred;
public void setDateOccurred(String dateTime) {
this.dateOccurred = LocalDateTime.parse(dateTime, FORMATTER);
}
}
Why didn’t your code work?
ISO 8601 format has a T between the date and the time. There’s no T in your date-time string, so DateTimeFormatter.ISO_DATE_TIME cannot parse it.
Variants
Now we’re at it, I’d like to show a couple of other options. Taste differs, so I can’t tell which one you’ll like the best.
You may put in the T to obtain ISO 8601 format. Then you will need no explicit formatter. The one-arg LocalDateTime.parse() parses ISO 8601 format.
public void setDateOccurred(String dateTime) {
dateTime = dateTime.replace(' ', 'T');
this.dateOccurred = LocalDateTime.parse(dateTime);
}
Or sticking to the formatter and the space between date and time, we can define the formatter in this wordier way:
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
What we get for the extra code lines is (1) more reuse of built-in formatters (2) this formatter will accept time without seconds and time with a fraction of second too because DateTimeFormatter.ISO_LOCAL_TIME does.
Link
Wikipedia article: ISO 8601
I just learned that this is also an option for the parsing :)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "uuuu-MM-dd HH:mm:ss")
private LocalDateTime dateOccurred;

Date time format for query parameter in rest url

I am using joda DateTime as parameter.
Use Custom Date Time format for request query parameter in spring REST
I have tried with #DateTimeFormat annotation.
I have tried with custom converter.
#GetMapping("/users/time")
public ResponseEntity<User> findByTimeRange(final DateTime from, final DateTime to) {
}
Spring REST only support "2019-09-01T7:32:56.235-05:30" but i want to use "2019-09-01T7:32:56"
Basically, you would need a custom formatter and pass parameters as Strings and parse them with it. Here is the working code using Joda Time and tested with your input:
private static final DateTimeFormatter FORMATTER =
DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
#GetMapping("/users/time")
public ResponseEntity<User> findByTimeRange(#RequestParam("from") String fromParam,
#RequestParam("to") String toParam) {
DateTime from = FORMATTER.parseDateTime(fromParam);
DateTime to = FORMATTER.parseDateTime(toParam);
// Do your search part...
}

SpringBoot2 #DateTimeFormat annotation with LocalDateTime doesn't work as i expected

i want to convert LocalDateTime to YYYY-MM-DD HH:mm:ss format with #DateTimeFormat.
following is my source code.
Entity Class
#Entity // JPA Annotation
#Setter // Lombok Annotations
#Getter
public class CustomModel {
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime addDate;
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime lastModifyDate;
}
Controller Class
...
CustomModel customModel = new CustomModel();
customModel.setAddDate(LocalDateTime.now());
customModel.setLastModifyDate(LocalDateTime.now());
...
and following is result.
In above image, i expected '2019-08-11 03:08:56'.
But result format is '2019-08-11 03:08:56.814944'.
what is wrong??
You didn't put the thymeleaf template, but the #DateTimeFormat annotation will only be taken into account if you use double bracket rather than simple bracket, i.e. ${{model.addDate}} rather than ${model.addDate}.
You can use DateTimeFormatter class. But to convert, you need String representation of your date. Then parse it.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
customModel.setAddDate(LocalDateTime.parse(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)), formatter);
customModel.setLastModifyDate(LocalDateTime.parse(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)), formatter);

Jackson: deserialize epoch to LocalDate

I have following JSON:
{
"id" : "1",
"birthday" : 401280850089
}
And POJO class:
public class FbProfile {
long id;
#JsonDeserialize(using = LocalDateDeserializer.class)
LocalDate birthday;
}
I am using Jackson to do deserialization:
public FbProfile loadFbProfile(File file) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
FbProfile profile = mapper.readValue(file, FbProfile.class);
return profile;
}
But it throws an exception:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token
(VALUE_NUMBER_INT), expected VALUE_STRING: Expected array or string.
How can I deserialize epoch to LocalDate? I would like to add that if I change the datatype from LocalDate to java.util.Date it works perfectly fine. So maybe it's better to deserialize to java.util.Date and create the getter and setter which will do the conversion to/from LocalDate.
I've managed to do it writing my own deserializer (thank you #Ole V.V. to point me to the post Java 8 LocalDate Jackson format):
public class LocalDateTimeFromEpochDeserializer extends StdDeserializer<LocalDateTime> {
private static final long serialVersionUID = 1L;
protected LocalDateTimeFromEpochDeserializer() {
super(LocalDate.class);
}
#Override
public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return Instant.ofEpochMilli(jp.readValueAs(Long.class)).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
Notice about timezone is also very useful. Thank you!
The still open question is if it can be done without writing own deserializer?
Another option that I went with if you have the ability to change the POJO, is to just declare your field as java.time.Instant.
public class FbProfile {
long id;
Instant birthday;
}
This will deserialize from a number of different formats including epoch. Then if you need to use it as a LocalDate or something else in your business logic, simply do what some of the converters above are doing:
LocalDate asDate = birthday.atZone(ZoneId.systemDefault()).toLocalDate()
or
LocalDateTime asDateTime = birthday.atZone(ZoneId.systemDefault()).toLocalDateTime()
I found a way to do it without writing a custom deserializer, but it'll require some modifications.
First, the LocalDateDeserializer accepts a custom DateTimeFormatter. So, we need to create a formatter that accepts an epoch millis. I did this by joining the INSTANT_SECONS and MILLI_OF_SECOND fields:
// formatter that accepts an epoch millis value
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// epoch seconds
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
// milliseconds
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
// create formatter, using UTC as timezone
.toFormatter().withZone(ZoneOffset.UTC);
I also set the formatter with UTC zone, so it won't be affected by timezones and DST changes.
Then, I've created the deserializer and registered in my ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
// add the LocalDateDeserializer with the custom formatter
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(formatter));
mapper.registerModule(module);
I also had to remove the annotation from the birthday field (because the annotation seems to override the module configuration):
public class FbProfile {
long id;
// remove #JsonDeserialize annotation
LocalDate birthday;
}
And now the big issue: as the DateTimeFormatter accepts only String as input, and the JSON contains a number in birthday field, I had to change the JSON:
{
"id" : "1",
"birthday" : "401280850089"
}
Note that I changed birthday to a String (put the value between quotes).
With this, the LocalDate is read from JSON correctly:
FbProfile value = mapper.readValue(json, FbProfile.class);
System.out.println(value.getBirthday()); // 1982-09-19
Notes:
I couldn't find a way to pass the number directly to the formatter (as it takes only String as input), so I had to change the number to be a String. If you don't want to do that, then you'll have to write a custom converter anyway.
You can replace ZoneOffset.UTC with any timezone you want (even ZoneId.systemDefault()), it'll depend on what your application needs. But as told in #Ole V.V.'s comment, the timezone might cause the date to change.

Jaxb omit timezone

I have Date field in my class marked with jaxb annotation.
#XmlElement(name = "startTime")
public Date getStartTime() {
return startTime;
}
But in the result i get startTime wtihout timezone even if it have it in zoneinfo.
So after setting it like this:
startTime = new Date(System.currentTimeMillis());
I get that kind of result in JSON
"startTime":"2016-05-25T17:22:23"
How can i get format like this : "2016-05-25T17:22:23.848+06:00" ?
If you are working with Java8, have a look to the java.time package, and use the class Instant instead of Date.

Categories

Resources