Spring mvc ZonedDateTime conversion to UTC - spring

I woudl ike to convert all java8 ZonedDateTimes into UTC time zone on the server side of the application. I am successfully binding the Java 8 jsr310 date data types into a spring RestController using the #DateTimeFormat.
#RequestMapping(value = "rest/test-date", method = RequestMethod.GET)
public TestCollection findPrivilegesByRoleList(
#RequestParam(value = "local-date", defaultValue = "2015-05-10") #DateTimeFormat(iso = ISO.DATE) LocalDate requestParamDate,
#RequestParam(value = "local-date-time", defaultValue = "2015-05-16T15:55:56") #DateTimeFormat(iso = ISO.DATE_TIME) LocalDateTime requestParamLocalDateTime,
#RequestParam(value = "zoned-date-time", defaultValue = "2015-05-18T11:55:56-04:00") #DateTimeFormat(iso = ISO.DATE_TIME) ZonedDateTime requestParamZonedDateTime
)
For the ZonedDateTime class, I would like to shift all input ZonedDateTimes to UTC time, so the server side is always working in UTC timezone. Following best practice #3 - Store it in UTC
http://apiux.com/2013/03/20/5-laws-api-dates-and-times/#comments
For JSON deserialization, I have a custom deserializer for ZonedDateTime that shifts any timezone into UTC.
....
//Parse string into a zoned date time with the specified timezone offset - EST, CET, EDT, PST, ect.
ZonedDateTime zonedDateTimewithTimeZone = ZonedDateTime.parse(string, formatter);
//Shift the date time to UTC Time
return zonedDateTimewithTimeZone.withZoneSameInstant(ZoneId.of("UTC"));
What is the best way to do the conversion in the controller binding? I understand this may be forcing multiple responsibilities into the same class, however I want to avoid adding the
ZonedDateTime.withZoneSameInstant
call for every date in every controller.
Thanks

Related

Is there simple way of changing timezone between dto and entities at database?

I write application on Spring Boot with Spring Data(postgresql).
I have the following case. I want to store in database time at UTC timezone, and parse it to/from "America/San-Paulo" timezone in dto.
For example: in controller I get dto with LocalDateTime in America/San-Paulo timezone. And I want to save it in database in UTC timezone.
I can do in when mapping from dto to entity. But maybe there is another simple way like setting some properties of hibernate/spring?
Since Java 8, we have the Date/Time API under java.time!
(1) Convert the timezone in annotated #PrePersist, #PreUpdate, and #PostLoad methods.
For example, in annotated #PostLoad, convert from UTC to America/San-Paulo.
private static ZoneId UTC_ZONE = ZoneId.of("UTC");
private static ZoneId LOCAL_ZONE = ZoneId.of("America/San_Paulo");
private LocalDateTime dateTime;
#PostLoad
public void toLocal() {
dateTime = dateTime.atZone(UTC_ZONE).withZoneSameInstant(LOCAL_ZONE).toLocalDateTime();
}
(2) Assuming you are using Jackson, you can write a custom serializer/deserializer.
UPDATE:
With PostgreSQL, you can use the type timestamp with time zone. By default, if you insert/update the column, it will convert the value to UTC.
In JPA:
#Column(columnDefinition = "timestamp with time zone")
UPDATE (22-07-01):
You could also use an AttributeConverter.

How to pass an Instant start, end date as #RequestParam

How can I pass an Instant startDate, endDate as a #RequestParam Instant startDate, Instant endDate with pattern yyyy-MM-dd so the request will looks like localhost:port/api/entity=startDate=2019-10-10?endDate=2019-10-15 any ideas how to perform it?
#RequestParam("from") #DateTimeFormat(iso = ISO.DATE) Instant startDate,
#RequestParam("to") #DateTimeFormat(iso = ISO.DATE) Instant endDate)
it does not help I'm getting Failed to convert value of type 'java.lang.String' to required type 'java.time.Instant' even tried with pattern. Also the problem is that when I've entity created at yyyy-MM-dd HH:mm:ss how can I ignore the seconds so when I pass date and hours only it will display the result.
Replace DateTimeFormat.ISO.DATE by DateTimeFormat.ISO.DATE_TIME.
The format that Instant takes : yyyy-MM-dd'T'HH:mm:ss.SSSXXX
The code below :
#GetMapping("/getlist")
public void method(#RequestParam("from") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant startDate,
#RequestParam("to") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant endDate)) {
// code
}
This should work.
Your date strings conform to ISO 8601 format for a date. Therefore, you should use LocalDate as the type i.e.
RequestParam("from") #DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
#RequestParam("to") #DateTimeFormat(iso = ISO.DATE) LocalDate endDate)
Here is an overview of the java.time types.
If you want to have a proper DateTime format to provide the capability of handling timezone for you, I suggest you use the following pattern:
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX") Date myDate
It's a common ISO DATE_TIME Format and you can easily convert it to Instant afterward.
e.g. "2000-10-31T01:30:00.000-05:00"
You can use yyyy-MM-dd HH:mm:ss the date format that your want.
#RequestParam("from") #DateTimeFormat(iso = "yyyy-MM-dd HH:mm:ss")
Instant startDate,
#RequestParam("to") #DateTimeFormat(iso = "yyyy-MM-dd HH:mm:ss")
Instant endDate)

LocalDateTime to java.sql.Date in java 8?

How to convert LocalDateTime to java.sql.Date in java-8?
My search on internet mostly give me Timestamp related code or LocalDate to java.sql.Date. I'm looking for LocalDateTime to java.sql.Date.
There is no direct correlation between LocalDateTime and java.sql.Date, since former is-a timestamp, and latter is-a Date.
There is, however, a relation between LocalDate and java.sql.Date, and conversion can be done like this:
LocalDate date = //your local date
java.sql.Date sqlDate = java.sql.Date.valueOf(date)
Which for any given LocalDateTime gives you the following code:
LocalDateTime dateTime = // your ldt
java.sql.Date sqlDate = java.sql.Date.valueOf(dateTime.toLocalDate());
#M. Prokhorov's answer is correct, I just want to add a few points.
A java.sql.Date keeps only the day, month and year values. The time values (hour, minute, seconds and milliseconds) are all set to zero. So, when converting a LocalDateTime to a java.sql.Date, these fields are lost.
If you're doing a one-way conversion and don't mind losing those fields, then it's ok to do it:
LocalDateTime dt = // LocalDateTime value
// convert to Date (time information is lost)
java.sql.Date date = java.sql.Date.valueOf(dt.toLocalDate());
But if you want to restore the original LocalDateTime later, it's better to save the time fields separetely, so you can recover it:
LocalDateTime dt = // your LocalDateTime
// save time information (hour, minute, seconds, fraction of seconds)
LocalTime savedTime = dt.toLocalTime();
// convert to Date (time information is lost)
java.sql.Date date = java.sql.Date.valueOf(dt.toLocalDate());
// retrieve back the LocalDate (only day/month/year)
LocalDate localDate = date.toLocalDate();
// retrieve the LocalDateTime, with the original time values
LocalDateTime ldt = localDate.atTime(savedTime);
It is possible to convert from LocalDateTime to java.sql.date while retaining the time part without havng to make assumptions about the time-zone by using java.util.Date as an intermediary:
LocalDateTime dateValue = // your LocalDateTime
java.util.Date utilDate;
String dateFormat = "yyyy-MM-dd'T'HH:mm:ss";
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern(dateFormat);
SimpleDateFormat sdf1 = new SimpleDateFormat(dateFormat);
try {
utilDate = sdf1.parse(dateValue.format(dtf1));
} catch (ParseException e) {
utilDate = null; // handle the exception
}
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

How do I make a spring-boot request method accept an iso formatted date param?

I'm trying to write a controller request method that accepts a date parameter that is sent as an iso formatted date time string. It looks like you can specify a format manually, annotating the method parameter with #DateTimeFormat(pattern="yyyy-MM-dd") but I want to use the iso
setting. I.e. #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME). Using the ISO Date Time format I can't even get it to parse the example date in the documentation. I'm wondering if I'm doing something wrong.
Here is my controller class:
#RestController
public class DateController {
#RequestMapping(path = "/echoIsoDate", method = RequestMethod.GET)
public ResponseEntity<String> echoIsoDate(#RequestParam("date") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Date date){
return new ResponseEntity<>(date.toString(), HttpStatus.OK);
}
#RequestMapping(path = "/echoDumbDate", method = RequestMethod.GET)
public ResponseEntity<String> echoDumbDate(#RequestParam("date") #DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
return new ResponseEntity<>(date.toString(), HttpStatus.OK);
}
}
When I try to call this controller with the date I actually want to parse it doesn't work:
http://localhost:8080/echoIsoDate?date=2015-12-30T00:00:00.000Z
When I try to call this controller with the example date from the documentation it doesn't work:
http://localhost:8080/echoIsoDate?date=2000-10-31%2001:30:00.000-05:00
The second controller method does work. e.g calling http://localhost:8080/echoDumbDate?date=1970-01-01 returns Thu Jan 01 00:00:00 CST 1970 (But then it's in CST, which presumably is in my system timezone).
Questions:
What am I doing wrong in echoIsoDate()? Or is there a bug in Spring?
For echoDumbDate() is there a way to specify the timezone I want, so that it will always use UTC?
try this instead
#RequestMapping(path = "/echoIsoDate", method = RequestMethod.GET)
public ResponseEntity<String> echoIsoDate(#RequestParam("date") #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") Date date){
return new ResponseEntity<>(date.toString(), HttpStatus.OK);
}

how to avoid rejected value [] issue in spring form submission

i have two dates in form submission in Spring 3 + Hibernate.
#Column(name = "FinStartDate")
private Date finStartDate;
#Column(name = "FinEndDate")
private Date finEndDate;
I'm display/hide dates on the basis of some criteria. When the dates are hidden and submit the form, the following errors
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'register' on field 'obj.finEndDate': rejected value []; codes [typeMismatch]
How to avoid the issue.
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
#DateTimeFormat(pattern = "dd.MM.yyyy")
private Date finEndDate;
Maybe, you should use serializer/deserializer.
I think that you miss a formatter to convert the date String to a Date object.
You can try to annotate your field
#DateTimeFormat(pattern = "yyyy-MM-dd")
or to declare a initbinder in your controller like :
#InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(
dateFormat, false));
}
Or you can declare a formatter in you mvc configuration file that will format every Date object your application is binding to.
Add #DateTimeFormat annotation for following way. If not working update date format. (MM-dd-yyyy, dd-MM-yyyy)
#Column(name = "FinEndDate")
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date finEndDate;

Resources