ZonedDateTime retrieved with one hour shift in Hsqldb with 2.6.x - spring-boot

I use Spring boot and JPA and my Order domain object have this field:
#Column(name = "TIMESTAMP")
private ZonedDateTime timestamp;
I create a date with this method:
protected static ZonedDateTime createZonedDateTime(final String date) {
LocalDateTime ldt = LocalDateTime.parse(date + " 00:00", DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"));
return ldt.atZone(ZoneId.systemDefault());
}
When I insert this in database throw JPA, with showsql to true, I can see that the inserted date is: [2020-01-01T00:00+01:00[Europe/Paris]]
when I retrieve the data throw JPA repository, the date I get is: 2020-01-01T01:00+01:00[Europe/Paris]
How come I have one hour shift? It's not working since hsqldb 2.6.0. Works perfectly with 2.5.0.
I use Liquidbase to create the field with:
<column name="TIMESTAMP" type="TIMESTAMP WITH TIME ZONE"
remarks="The timestamp of the order"/>

Related

Find all entities with dates between, spring data jpa [duplicate]

I'm trying to make a query to retrieve some data which has been created between two dates (represented as Instant).
Here below an extract from the Entity I'm using:
#Entity
public class HistoricalData {
#Id
#GeneratedValue
private Long id;
#Column
private String name;
#CreationTimestamp
private Instant timestamp;
#Column
private Double price;
}
And the query I've written to retrieve the data between the two Instants;
#Query("select h from HistoricalData h where h.timestamp between :timestampStart and :timestampEnd and upper(name) = upper(:name)")
List<HistoricalData> findHistoricalDataBetween(#NonNull Instant timestampStart, #NonNull Instant timestampEnd, #NonNull String name);
Which produces this SQL query:
select historical0_.id as id1_5_, historical0_.price as price2_5_, historical0_.timestamp as timestam3_5_ from historical_data historical0_ where (historical0_.timestamp between ? and ?) and upper(historical0_.name)=upper(?)
Also I wrote the "hibernate JPA" query just to try but no success:
List<HistoricalData> findHistoricalDataByTimestampAfterAndTimestampBeforeAndName(#NonNull Instant timestampStart, #NonNull Instant timestampEnd, #NonNull String name);
Keep in mind that all the above queries compile correctly and do not throw any exception, they just retrieve nothing from the database
The database I'm using is a latest version of MariaDB and the connector version is the 2.7.2
Also the SpringBoot version I'm using is the 2.5.3
Here is DDL from the table definition (automatically generated from Hibernate):
create table historical_data
(
id bigint not null primary key,
price double null,
timestamp datetime not null,
name varchar not null
);
An this is how the timestamp looks like in the database:
Even though records between those two Instants are present in the database I'm still getting nothing as a result from the query.
Looks like the reason is a time zone.
MySQL driver uses incorrect time zone transformations, using a default local time zone in place of a connection time zone (or vice versa).
Just debug this query inside MySQL driver to have fun and figure out what happens.
You can add parameters to the database URL to see which actual values are passed for the prepare statement
jdbc:mysql://<DATABASE_URL>?logger=com.mysql.cj.log.Slf4JLogger&profileSQL=true

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.

Converting java.sql.timestamp to millisecond format

I have a private java.sql.Timestamp myDate in some model (POJO) class like below
private String isActive;
private Date dob;
private Timestamp createdDate;
private Timestamp lastMktPrefUpdateAt;
We were using spring boot version to 1.5.10.RELEASE and REST API response for timestamp field was in millisecond format like below -
{
"isActive": "y",
"lastMktPrefUpdateAt": 1632195609787,
"dob": "08/12/1991",
"createdDate": 1632195609788
}
We have recently upgraded the spring boot version to 2.3.0.RELEASE and this started sending timestamp field in response to some ISO format like below -
{
"isActive": "y",
"lastMktPrefUpdateAt": "2021-09-20T22:10:09.787+0000",
"dob": "07/12/1991",
"createdDate": "2021-09-20T22:10:09.788+0000"
}
Can someone please help answering, how can we format Timestamp back to millisecond format?
We need to format Timestamp at global level meaning without changing all the POJO classes.
Try this in your properties file
spring.jackson.serialization.write-dates-as-timestamps:true
OR
Use #JsonFormat(shape = JsonFormat.Shape.NUMBER) above the property that you want.
Update
If you want to add millis to your timestamp, try to pass the long value to the Timestamp constructor and call the getTime() method to receive a 13-digits timestamp.
long now = System.currentTimeMillis();
Timestamp timeStamp= new Timestamp(now);
System.out.println(timeStamp.getTime());
You could convert that string to a ZonedDateTime object which has a .toInstant().toEpochMilli() method.
Something like:
long millis = ZonedDateTime.of(LocalDateTime.parse(str), ZoneId.of("Etc/UTC")).toInstant().toEpochMilli();
see:
How to get milliseconds from LocalDateTime in Java 8
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html
However, I would recommend refactoring the system to use the immutable LocalDatTime object at a later point.
(There may be an appropriate annotation you can put on that timestamp field to parse it in a specific way, but not one that I am aware of.)

Hibernate4 timestamp with joda time

does any one know about how to implement create & update timestamps with spring mvc4, hibernate4 and joda-time
#CreationTimestamp
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime created_at;
public void setCreated_at(LocalDateTime created_at) {
this.created_at = created_at;
}
When i tried in the above way, i got errors

JPA 2.0 and Oracle with TemporalType.TIME

I'm Using Oracle 11g and JPA 2.0 (hibernate in JBoss 6.0.0).
I need to represent a time range in an entity, so I defined those fields:
#Temporal(TemporalType.TIME)
private Date startTime;
#Temporal(TemporalType.TIME)
private Date endTime;
The generated tables use two DATE fields, and this is ok since Oracle doesn't have a type representing just the time part.
When loading the entity from db, just the time part is loaded (the field contains a java.sql.Time).
I've seen instead that if I set a complete date+time in the fields, the date part will be persisted to the db.
Is there a way to ensure that the date part will not be persisted to the db?
You can write setter methods which remove the date component. Quick and dirty example:
public void setStartTime(Date startTime)
{
this.startTime = new Time(startTime.getTime() % 86400000L);
}
Though you'd be better off using Joda Time to do your date/time calculations (see this question). I didn't test this to make sure it's correct, but it should show you the basic idea:
public void setStartTime(Date startTime)
{
this.startTime = new Time(LocalTime.fromDateFields(startTime).millisOfDay().get());
}

Resources