How can I configure JPA/Hibernate to store a date/time in the database as UTC (GMT) time zone? Consider this annotated JPA entity:
public class Event {
@Id
public int id;
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date date;
}
If the date is 2008-Feb-03 9:30am Pacific Standard Time (PST), then I want the UTC time of 2008-Feb-03 5:30pm stored in the database. Likewise, when the date is retrieved from the database, I want it interpreted as UTC. So in this case 530pm is 530pm UTC. When it's displayed it will be formatted as 9:30am PST.
You would think this common problem would be taken care of by Hibernate. But its not! There are a few "hacks" to get it right.
The one I use is to store the Date as a Long in the database. So I am always working with milliseconds after 1/1/70. I then have getters and setters on my Class that return/accept only Dates. So the API remains the same. The down side is that I have longs in the database. SO with SQL I can pretty much only do <,>,= comparisons -- not fancy date operators.
Another approach is to user a custom mapping type as described here: http://www.hibernate.org/100.html
I think the correct way to deal with this is to use a Calendar instead of a Date though. With the Calendar you can set the TimeZone before persisting.
NOTE: Silly stackoverflow won't let me comment, so here is a response to david a.
If you create this object in Chicago:
Hibernate persists it as "12/31/1969 18:00:00". Dates should be devoid of timezone, so I'm not sure why the adjustment would be made.
To the best of my knowledge, you need to put your entire Java app in UTC timezone (so that Hibernate will store dates in UTC), and you'll need to convert to whatever timezone desired when you display stuff (at least we do it this way).
At startup, we do:
And set the desired timezone to the DateFormat:
Adding an answer that's completely based on and indebted to divestoclimb with a hint from Shaun Stone. Just wanted to spell it out in detail since it's a common problem and the solution is a bit confusing.
This is using Hibernate 4.1.4.Final, though I suspect anything after 3.6 will work.
First, create divestoclimb's UtcTimestampTypeDescriptor
Then create UtcTimestampType, which uses UtcTimestampTypeDescriptor instead of TimestampTypeDescriptor as the SqlTypeDescriptor in the super constructor call but otherwise delegates everything to TimestampType:
Finally, when you initialize your Hibernate configuration, register UtcTimestampType as a type override:
Now timestamps shouldn't be concerned with the JVM's time zone on their way to and from the database. HTH.
With Hibernate 5.2, you can now force the UTC time zone using the following configuration property:
For more details, check out this article.
I encountered just the same problem when I wanted to store the dates in the DB as UTC and avoid using
varchar
and explicitString <-> java.util.Date
conversions, or setting my whole Java app in the UTC time zone (because this could lead to another unexpected issues, if the JVM is shared across many applications).So, there is an open source project
DbAssist
, which allows you to easily fix the read/write as UTC date from the database. Since you are using JPA Annotations to map the fields in the entity, all you have to do is to include the following dependency to your Mavenpom
file:Then you apply the fix (for Hibernate + Spring Boot example) by adding
@EnableAutoConfiguration
annotation before the Spring application class. For other setups installation instructions and more use examples, just refer to the project's github.The good thing is that you don't have to modify the entities at all; you can leave their
java.util.Date
fields as they are.5.2.2
has to correspond to the Hibernate version you are using. I am not sure, which version you are using in your project, but the full list of provided fixes is available on the wiki page of the project's github. The reason why the fix is different for various Hibernate versions is because Hibernate creators changed the API a couple of times between the releases.Internally, the fix uses hints from divestoclimb, Shane and a few other sources in order to create a custom
UtcDateType
. Then it maps the standardjava.util.Date
with the customUtcDateType
which handles all the necessary time zone handling. The mapping of the types is achieved using@Typedef
annotation in the providedpackage-info.java
file.You can find an article here which explains why such a time shift occurs at all and what are the approaches to solve it.
Hibernate does not allow for specifying time zones by annotation or any other means. If you use Calendar instead of date, you can implement a workaround using HIbernate property AccessType and implementing the mapping yourself. The more advanced solution is to implement a custom UserType to map your Date or Calendar. Both solutions are explained in this blog post: http://dev-metal.blogspot.com/2010/11/mapping-dates-and-time-zones-with.html