Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA TemporalType.Date giving wrong date

Tags:

java

mysql

jpa

I have a class that has a date field representing a "valid from" date for a piece of data. It is defined like this:

@Temporal( TemporalType.DATE )
private Date validFrom;

All seems to be working fine right up the the point where I pull the date from the database and display it. If I select the date 18-Sep-2003 in the front end then save it when I check in the database sure enough that is the date held (database is MySQL 5.5.9 column type is DATE). However when I pull up a list records the date shown is 17 Sep 2003 - one day earlier.

If I choose a date early or late in the year like 26 Mar 2003 or 25 Dec 2003 everything is fine so I guessing this is something to do with daylight saving but where is the error creeping in? Since the database appears to be holding the correct date I'm guessing it must be when JPA is converting back into a java.util.Date - is java.util.Date the best class to use for a date? I've seen a few examples where people use Calendar but that seems pretty heavy weight and I'm not sure how well it will work with a JSF based front end.

like image 414
wobblycogs Avatar asked Mar 14 '11 14:03

wobblycogs


People also ask

What is temporaltype annotation in spring data JPA?

This annotation takes a parameter which is a value of TemporalType enum: A detailed article about dates and times mapping with JPA can be found here. 3. In Practice In practice, once our entities are correctly set up, there is not much work to do to query them using Spring Data JPA.

How do I set @temporal in JPA?

The java.util or java.time properties need to explicitly mark the SQL type correlation with the @Temporal annotation prior to Java 8. In JPA or Hibernate, @Temporal should only be set on a java.util.Date or java.util.Calendar property.

What are the different temporal types supported by JPA?

Different temporal types in JPA. Temporal information is very precious for all marketing purposes, as birthday e-mailing or several other reminders. JPA has specific annotation to handle this type - @Temporal. Through this article we'll explore 3 temporal types supported by JPA specification: DATE, TIME and TIMESTAMP.

What are the disadvantages of JPA with temporal annotation?

The disadvantage is that we need to provide the @Temporal annotation to instruct the JPA provider about the associated database column type. As explained in this article, JPA 2.2 adds support for Java 8 Date/Time API. So, we can map the subscribed_on to a LocalDate and the published_on column to a LocalDateTime:


2 Answers

Very sorry but all of the answers so far are generally incorrect. The answer is quite simple but requires that we separate five points:

  1. DATE = java.sql.Date, which is a wrapper around java.util.Date that is the number of milliseconds since the Epoch in the UTC time-zone. So this has the year/month/date/hours/minutes/seconds in a fixed GMT+0 (UTC) time-zone. Note however that java.sql.Date sets the time components to zero!
  2. TIMESTAMP = java.sql.TimeStamp which is a component wrapper around Date that adds fractional seconds to support the SQL DATE type standard. This class/type is not relevant or needed for this question but in short this has the date plus the time.
  3. The database stores DATE objects as defined (using UTC as the offset from Java) but may translate the time if configured in the database to be in a different time-zone. By default most databases default to the local server timezone, which is a very bad idea. Ladies, gentlemen ... ALWAYS store DATE objects in UTC. Read on...
  4. The time in the JVM and timezone needs to be right. Since the Date object is using UTC, is an offset getting calculated for your server-time? Consider that with the strong recommendation that server time be set to GMT+0 (UTC).
  5. Finally when we want to render the DATE from the database (using JSF or whatever), it should be setup to be GMT+0 timezone and, if done from the server up side also ... your dates and times will ALWAYS be consistent, referential and all good things. All that is left is to render the time and THIS is where the user-agent (for a web-application for example) could be used to translate the GMT+0 time to the users "local" timezone.

Summary: Use UTC (GMT+0) on the server, in the database, in your Java objects.

DATE and TIMESTAMP are only different from a database perspective in that TIMESTAMP carries additional fractions of seconds. Both use GMT+0 (implied). JodaTime is a preferred calendar framework to deal with all of this but won't fix the issues of mismatched JVM to database time-zone settings.

If application designs from JVM to the DB do not use GMT, due to daylight-savings, clock adjustments and all kinds of other regional games that are played in the world local clocks ... the times of transactions and everything else will forever be skewed, non-referential, inconsistent, etc.

Another good related answer about data types: java.util.Date vs java.sql.Date

Also note that Java 8 has updates with better date/time handling (finally) but this does not fix having the server clock the JVM is running on be in one timezone and the database be in another. At this point there is always translation happening. In every large (smart) client I work with, the database and JVM server timezones are set to UTC for this very reason, even if their operations largely occur in some other timezone.

like image 143
Darrell Teague Avatar answered Sep 18 '22 10:09

Darrell Teague


After much experimenting and searching I'm pretty sure I've found the cause of the problem. The date is held in a java.util.Date which comes with all the baggage of time and a timezone. It would seem that JPA is reading the date 18 Sep 2003 from the database and then populating the date like this: "Thu Sep 18 00:00:00 BST 2003" - notice the timezone has been set to BST probably because it wasn't explicitly set by the database. Anyway, it is necessary to format the output in the JSF page if you only want to see the date like this:

<h:outputText value="#{t.validFrom}">
    <f:convertDateTime pattern="dd MMM yyyy"/>
</h:outputText>

This, however, assumes that the timezone is whatever is currently in force on the machine. In my case the timezone is currently GMT (because it's winter) so when presented with the date "Thu Sep 18 00:00:00 BST 2003" it converts it to GMT by subtracting one hour leaving the display showing 17 Sep 2003.

like image 44
wobblycogs Avatar answered Sep 20 '22 10:09

wobblycogs