Unwanted automatic time zone change using Hibernate / JPA and JDK Date

I use Hibernate (4.2) as my persistence provider, and I have a JPA object that contains a Date field:

@Entity @Table(name = "MY_TABLE") public class MyTable implements Serializable { . . . @Temporal(TemporalType.TIMESTAMP) @Column(name = "START_DATE") private Date startDate; public Date getStartDate() { return startDate; } public void setStartDate(Date startDate) { this.startDate = startDate; } . . . } 

The column corresponding to START_DATE is defined as START_DATE TIMESTAMP (without time zone).

I use Joda-Time (2.3) for my application to deal with the date (always in UTC), and just before saving the Entity I use the toDate() method of the Joda DateTime class to get the JDK Date object to obey the display:

 public void myMethod(DateTime startDateUTC) { . . . MyTable table = /* obtain somehow */ table.setStartDate(startDateUTC.toDate()); . . . } 

When I look at the stored value in the database, I notice that somewhere (JDK? Hibernate?) Converts the Date value using the default time zone of the JVM where the code is executed. In my case, this is America / Chicago.

The problem does occur near daylight saving time (DST). For example, if time is inside

 2014-03-09T02:55:00Z 

it is stored as

 09-Mar-14 03:55:00 

I would like it to be stored as

 09-Mar-14 02:55:00 

However, in the CDT, 2:55 a.m. March 9 does not exist ("Spring Forward"). So something (JDK? Hibernate?) Takes the date forward.

I would like the moment that is stored in the database to be in UTC. In the end, this is how I deal with it inside my application, but as soon as I submit it for storage, it will be converted to my default time zone.

Note: I cannot set TimeZone by default using

 TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 

because the JVM I'm running on is shared between multiple applications.

How to store date in UTC without setting default JVM timezone in UTC?

+7
java date datetime hibernate jpa
source share
3 answers

I ran into this myself. I saw that even if you indicated UTC as the time zone in your date (and you can see it by printing it and seeing "Z" at the end), for some reason the JVM wants to take over and convert the date for you using JVM default timezone.

In any case, you need to create a custom mapping to get around this. Try using Jadira :

 @Entity @Table(name = "MY_TABLE") public class MyTable implements Serializable { . . . @Column(name = "START_DATE") @Type(type="org.jadira.usertype.dateandtime.legacyjdk.PersistentDate") private Date startDate; public Date getStartDate() { return startDate; } public void setStartDate(Date startDate) { this.startDate = startDate; } . . . } 

By default, the Jadira PersistentDate class uses UTC as the time zone when it converts the date to a millisecond value that is stored in the database. You can specify other time zones, but this is similar to UTC - this is what you want to keep.

As follows from the commentary on your post, sometimes the tool that you use to query the database does a stupid, stupid automatic conversion based on the fact that I-mine-JDK-default-TZ is for you, which suggests that the value is still wrong.

You can also try storing the original value (as INTEGER) to make sure that the correct value in milliseconds is stored.

NTN

Mos

+6
source share

There is an article about this unexpected time zone change problem that you can check here here . He gives an explanation of the root of the problem and shows how to deal with it. Of course, the main presumption is that we want to store dates in UTC format in a database.

For example, whenever you read a date from a database (say: 9:54 UTC), JDBC skips any time zone information. So what the JVM receives through JDBC is the date interpreted as if it were in the local time zone (for my case, 9:54 UTC + 2). If the local time zone is different from UTC (and this usually happens), we get the wrong time shift.

A similar situation occurs when writing to the database.

There is a small open source DbAssist project containing fixes for different versions of Hibernate. Therefore, if you are using Hibernate 4.2.21, just add the following Maven dependency to your POM file and your problem will be solved (detailed installation instructions, for example, Spring Boot, can be found in the github library).

 <dependency> <groupId>com.montrosesoftware</groupId> <artifactId>DbAssist-4.2.21</artifactId> <version>1.0-RELEASE</version> </dependency> 

After applying this hotfix, your java.util.Date fields in entity classes will be counted and stored as expected: as if they were saved in UTC in the database. If you use JPA annotations, you do not need to change the display in the entity (as in one of the previous answers); this is done automatically.

Internally, the fix uses a custom UTC date type, which overrides the hibernation date type to force it to process all dates in the database in UTC format. Then, to apply the mapping from java.util.Date to UtcDateType , it uses the @Typedef annotation. See below:

 @TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class), package com.montrosesoftware.dbassist.types; 

If your project depends on HibMate Hibernate files or other versions of Hibernate, go to the github wiki project for more detailed instructions on how to install the correct fix.

+4
source share

Have you tried to use getDateTime (DateTimeZone x) from a DateTime object?

Something like that:

 public void myMethod(DateTime startDateUTC) { . . . MyTable table = /* obtain somehow */ table.setStartDate(startDateUTC.toDateTime(DateTimeZone.UTC)); . . . } 

Hope this helps you!

-2
source share

All Articles