Spring Boot - Jackson - datetime timezone problems - spring

I am having trouble dealing with timezones across my projects.
First of all, I have two use cases:
1) A user input a DateTime with datepicker in HTML and we want to save it without timezone in the database. example input: 10.04.2018 18:00 --> we want to save exactly this date and also want to retrieve exactly this date, no matter what the timezone is on the client side. (we can think about this like a never changing date string)
2) The more common, second use case is, we enter a DateTime with datepicker in HTML and want to save the date with timezone in the database. For example:
The Clients Timezone is Europe/Istanbul and he inputs a DateTime: 10.04.2018 18:00. Now we want to save this date as Europe/Berlin in Database which would be 10.04.2017 17:00. When we retrieve the date we want to convert the date to the timezone of the client. In this example, if a client with timezone Europe/Berlin wants to retrieve the database date 10.04.2017 17:00 we would exactly retrieve: 10.04.2017 17:00. But if a client with timezone Europe/Istanbul wants to retrieve the same value 10.04.2017 17:00 we would retrieve: 10.04.2018 18:00
Now I tried to achieve this behavior by settings the webapp timezone to Europe/Berlin and parse the dates for each use case with the Moment.js library on client site. Therefore, I always expect to retrieve a Date in the timezone Europe/Berlin from my web service and then either convert to the local time zone or let the date as it is.
I am using Spring Boot with following version:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
In my application.properties file i set those values:
spring.jpa.properties.hibernate.jdbc.time_zone=Europe/Berlin
spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
spring.jackson.time-zone=Europe/Berlin
MyApplication:
#PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
}
My attributes in my model class look like this:
#Column(name = "timeFrom")
#Temporal(TemporalType.TIMESTAMP)
private Date timeFrom;
#Column(name = "timeTo")
#Temporal(TemporalType.TIMESTAMP)
private Date timeTo;
For the first use case, where I want a date which is not parsed to the local time zone i use this code to retrieve my date in javascript: (because I know that my date in database is Europe/Berlin, I convert it to this time zone in order to retrieve the exact same date as it is in the database)
var countHours = moment(timeTracking["countHours"]).tz('Europe/Berlin').format('HH:mm');
For the second use case, where i want to convert my Europe/Berlin date from the database to the local client time zone i use this code:
var timeFrom = moment(timeTracking["timeFrom"]).format('YYYY-MM-DD HH:mm');
When i pass a date from javascript via JSON and ajax to my backend i use this:
moment($("#timeRecord-timeFrom").val()).valueOf()
On serverside i receive a timestamp in milliseconds and i parse it like that:
Date dateFrom = new Date(node.get("startTime").asLong());
Now the weird part: everything I posted above is working perfectly fine on my localhost (local machine). However, as soon as I push my war file to the live system (Jelastic Spring Boot Server) I have an offset of 2 hours in my date.
The strangest part is when I commit my date via ajax and retrieve the persisted object afterward everything is fine and I have no offset. But when I do a full page reload, I immediately receive the date with 2 hours offset. However, the date is still correct in the database.
As this is only happening on the live system and not on the localhost I have no clue how to fix this.
What am I doing wrong? Would it be better to switch to JodaTime or Java8 LocalDateTime? I already tried it but ran into several build errors so stick with java.util.Date.
A best practice tutorial for my use cases would be also appreciated.

Related

Carbon isn't giving the correct datetime with setTimezone

I'm using Carbon inside of my Laravel API project, when trying to set the timezone and get a datetime back in the user's timezone, I'm for some reason getting a UTC value back, what am I doing wrong?
// $user->timezone will give me "America/Curacao" or whatever their time zone is
$curr = Carbon::now()->setTimezone($user->timezone)
But when I echo out the contents of $curr, I'm getting a UTC time of:
2021-04-05T11:58:35.186750Z
What am I missing?
You get the ISO-8601 string with is the standard format for dates in JSON. But the object is correctly in America/Curacao timezone. If you want to pass it to a Date object in the browser, you don't need the timezone and can just pass this string as is.
If you want to format the date to be seen by the user, then just pick the format after setting the timezone:
$curr = Carbon::now()->setTimezone($user->timezone)->format('Y-m-d H:i:s')
Or whatever format would be relevant for you.
And you should keep 'timezone' => 'UTC' in your app settings as your users will likely have any various timezone, and UTC is the more agnostic to use to save them back-end side.

Date conversation with time zone information

I have one issue. Solutions might be there already in this forum but I couldn't find anything. Please share if it's already answered.
The scenario is as below.
Request is coming in CET to my application which is running in UTac time zone. My backend stored procedure is running in CET zone. The time that is passed in as string with cet offset I need to send same as SQL date with same date to my backend.
Current implementation is:
Convert string date to offsetdate. Then create Instant from that offsetdate and then util date using that instant. Once I have until date I am converting that to SQL Date.
So here issue is if input request us 2012-07-15T00:00:00+02:00 then when applications which is running in UTC is giving correct offset date with cet offset information but when it is converting to instant then always UTC will come. So new date is 2012-07-14T00:00:00Z and because if this my util and SQL date is also coming 1 day behind.
Can anyone please guide if there is any other way where I can create SQL or util. Date for same date irrespective of any time zone?
Because the expectation is request can come with any time zone and we need to understand that zone and then convert or use accordingly and same date should go to back end.
API developed with: Spring boot with JAVA 8 and backend we are accessing using stored procedure where stored procedure is expecting in SQL date.
Since you can use OffsetDate and other classes from java.time, the modern Java date and time API, you should no longer be using java.util.Date nor java.sql.Date. Those classes are poorly designed and long outdated, and the modern API offers all the functionality you need. You might have thought that you needed a java.sql.Date for storing into your SQL database. But with JDBC 4.2 (or a newer JPA implementation such as Hibernate) you can directly store a LocalDate there, which you will prefer.
String requestString = "2012-07-15T00:00:00+02:00";
OffsetDateTime dateTime = OffsetDateTime.parse(requestString);
LocalDate date = dateTime.toLocalDate();
System.out.println("Date is " + date);
This prints:
Date is 2012-07-15
No day has gone lost. To save do for example:
PreparedStatement ps = yourDatabaseConnection.prepareStatement(
"insert into your_table (your_date) values (?);");
ps.setObject(1, date);
ps.executeUpdate();
Sometimes we need to pass an old-fashioned type to a legacy API that we cannot afford to change right away. If this was your reason for wanting a java.sql.Date, convert like this:
java.sql.Date sqlDate = java.sql.Date.valueOf(date);
System.out.println("Old-fashioned java.sql.Date is " + sqlDate);
Old-fashioned java.sql.Date is 2012-07-15
Still no day is lost.
Edit: If what your legacy API requires is a java.util.Date, you will need to know the way to convert:
Instant startOfDay = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
Date oldFashionedUtilDate = Date.from(startOfDay);
System.out.println("As old-fashioned java.util.Date: " + oldFashionedUtilDate);
When I run with a default time zone UTC the output is:
As old-fashioned java.util.Date: Sun Jul 15 00:00:00 UTC 2012

Date timezone getting changed when using spring boot default jackson mapping

I have spring boot rest service which call another service xyz and receive date in format yyyy-MM-ddXXX from json. But time Zone of of date is getting changed in my service response. Suppose I am getting date in JSON from service xyz as "date": "2018-08-27-07:00" but my service response is returning date : "2018-08-27-04:00". Offset getting changed. Date field in my POJO is . I want to use the same offset I am getting from backend service and it can be any offset.
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-ddXXX")
private Calendar date;
The problem is that Calendar (and Date) use implicit time conversions to adjust it to your time zone. And almost always it is something that not expected.
To avoid this use java.time classes (such as OffsetDateTime or ZonedDateTime, or even LocalDateTime if you do not need to work wit time zones).
And small offtopic advice: try to use time format aligned to ISO8601 standard (like 2018-08-24T22:30:00)

Jodas LocalDateTime with Spring Data and Couchbase has no timezone

I wanna persistant an object with LocalDateTime fields with spring data and a couchbase behind in a spring boot app.
Here are the fieldmapping:
#Field
private LocalDateTime start;
#Field
private LocalDateTime end;
When I save the object then the dates are stored as numbers in couchbase.
Here are the stored data in couchbase:
"start": 1518818215508,
So the problem is, if I store an LocalDateTime e.g at 10.00, and then read it from db the result is 09:00 instead of 10:00 because of my local time +1:00.
In Postgres I would save the date in an column with mapping: columnDefinition= "TIMESTAMP WITH TIME ZONE"
How I can solve this problem in couchbase?
JSON (famously) has no date format (unlike relational databases which have a sprawling number of different formats). If you want to store timezone data in JSON, here are two options I can think of:
Using a string instead of a number, and then using something like ISO-8601.
Store the epoch time as you currently are as a UTC value, but also store a separate number field that represents a timezone offset from UTC
I would recommend going with the first approach.
I'm no Spring/Java expert, but a quick look at the documentation says you can do this by setting system property org.springframework.data.couchbase.useISOStringConverterForDate to true

UTC DateTime problems

I currently store all dateTimes in the DB as UTC dates. Each users time zone offset is also stored in the DB. When I retrieve a Date it is converted back to their local date using this offset.
The problem occurs when I retrieve a date using an ajax call. The date (which is already converted using the offset) is, I think, returned as a Java Date object. The browser then decides to mess with my Date adding the clients computers time zone offset to the Date object. This is causing dates to be a day ahead of what they should be if the time component is more than 11.59am.
The only solution I can come up with is to pass them as strings in which case this of course wouldn't happen. This is a laaaast resort for me though and I would love to find a better solution or workaround for this problem.
Your browser is not messing with the dates given that browsers don't have a native date transfer variable. You have something else that is doing that. How are you sending your dates in ajax? Json? Json will only send numbers or strings. XML will only send strings.
Something is converting your sent date into a javascript date object, find out what it is.

Resources