In java, I want to know that what is the best practice to keep date info for display, query, report etc. It seems that if we persist as long, all timezone dependency will be removed and we will keep 'persist globally, display locally' principle since Date object automatically converts long to current timezone.
But what is the advantage of persisting as Date object?
Do I loose any info other than info owner's timezone?
Can I get any wrong info when DLS takes into account?
Difference between persisting as UTC date and long is just readable db info?
Depending on your database you should use either TIMESTAMP WITH TIMEZONE or you convert it to UTC time and store it as long.
The first one relies on the DB to handle it correctly (the DB will, but will your DB driver? You have to test this for your setup). The second one makes it a manual process, you will get the correct result in the end but will have more hazzle with it because you have to take care about everything.
Inside Java you might want to use Calendar over Date because there you can specify the TIMEZONE etc. manually, thus you are able to display Dates in timezones different to your own easier.
Related
User inputs date from JSP page, and it converts to Joda DateTime, the string output is
2014-03-26T00:00:00.000+09:00
However when I persist this entity containing date filed in database, and retrieve and print out again, it becomes
2014-03-25T09:00:00.000+09:00.
I don't know why database make this change to minus one day.
I use postgres, hibernate JPA for application development.
Thanks in advance.
What is the value in the database? Use pgAdmin app, the psql command line tool, or some other database admin tool to query Postgres directly.
What data type are you using in Postgres? You probably should be using TIMESTAMP WITH TIME ZONE. Avoid using the WITHOUT time zone type as it ignores any time zone offset info you may provide.
Despite the name, neither type stores any time zone info. The difference is whether you want Postgres to pay any attention to time zone info on incoming data. Be sure to read the doc thoroughly and play with it to experiment until understand how date-time works.
Read Always Use TIMESTAMP WITH TIME ZONE by David E. Wheeler, a Postgres expert.
I saw here on SO a few related questions (like this one and of course, this one)... Essentially, what I want is to store date-time as UTC, and let application user choose the time zone he wants to display date-time in.
Since it seems that date-time fields are affected by the underlying JDBC driver, I wonder if this is an acceptable way to go about storing UTC date-time:
Set both MySQL and Application server machine to UTC time zone (no need to separate)
Both MySQL and JVM should pick up underlying system time settings (if not instructed otherwise)
Use DATETIME table columns on MySQL side
Use java.util.Date as corresponding mapping on Hibernate side (I guess java.sql.Timestamp could be used too)
Let the application worry about interpreting date-time fields - i.e. let the user choose preferred time zone
Is this OK?
EDIT
To clarify - here I meant to refer to timestamps created strictly on the server (e.g.date-time of record creation). So the application server instantiates Date objects (new Date() equals current date-time on the server, and this is really time zone agnostic).
Now if a client user wants to supply some date for searching/filtering purposes, here is where the transformation from client-local time to UTC should take place, IMHO...
I would suggest another simple approach which would independent of machine timezone settings.
Instead of setting the timezone of the server machine, set the timezone of JVM. This can be done via system properties. On Windows example would as follows
set JAVA_OPTS=%JAVA_OPTS% -Duser.timezone=GMT
For MySQL here is the reference for doing it.
Now this this make sure that all your date-time are in GMT
Keep the timezone as configurable property OR it can be user dependent as well. So you store timezone for each user if the users belong to different geographies.
Whenever, a date is needed, after you select it from the database, apply the timezone to get the correct time.
The advantage of this approach is that this will work for the all the timezone users. Meaning the user will see the correct time as per their timezone.
Use locales to implement internationalization.
It seems (and maybe I'm wrong) that if you want to preserve the timezone of when something happened with JDBC and Postgres you need to store the timezone separately from the timestamp.
That is I would prefer to give my ORM/JDBC/JPA a Java Calendar (or Joda DataTime) with say timezone America/New_York to a Postgres timestampz field. AND I would expect on retrieval regardless of the Servers timezone (or defaulting to UTC) to give me back a Calendar with timezone America/New_York. But just looking at most JDBC code (and things that depend on it that doesn't happen).
Is this correct?
This seems ridiculous that I would need to store the tz in another field when postgres supports it.
Thus it seems like the only two options are:
Select the timestampz Postgres column as a java.util.String and parse it.
Store the timezone as a separate field.
Option number one and two one would require some sort of conversion interceptors for my SQL mapping / ORM libraries.
What is the best solution for JDBC ?
What is the best solution for JPA (if different than JDBC)?
When you store a timestamp with time zone (timestamptz) it's converted to UTC for storage in the DB. When retrieved, it's converted to the client's current timezone, not the timezone it was originally in. It's a point in time, basically.
There is also timestamp without time zone (timestamp). This is not subject to conversion, but does not carry a timestamp with it. If you store a timestamp with your client time zone set to UTC, then retrieve it when the client time zone is '+08:00', you get the same value. That's half what you want, in that it preserves the raw time value.
The names and behaviours are awful and confusing, but set by the SQL standard.
You must store the time zone separately if you wish to record a point in time at a particular time zone. I'd recommend storing it as an INTERVAL with a CHECK constraint limiting it to be colname BETWEEN INTERVAL '-12' HOUR + INTERVAL '1' SECOND AND INTERVAL '12' HOUR. That definition rejects -12:00 and accepts +12:00; I'm not totally sure that's right, so check.
You could either store the timestamp of local time at that time zone (what I'd probably do), or store the timestamptz of the UTC time when the event occurred plus an offset that lets you convert it to local time.
Either will work fine for JDBC. For JPA, it'll depend on how well your provider understands and maps interval types. Ideally you want a transient generated field in your entity that reconstructs the Calendar instance you want using the timestamp and interval stored in the database.
EclipseLink supports storing the timezone in Oracle, I think you could get it to be stored in Postgres as well if you customized your PostgreSQLPlatform.
Changes to time zones do occur from time to time, I know every year in the UK there seems to be talk that we should scrap GMT or use "double summer time".
When these changes occur, I guess that any dates obtained via the TimeZone or Calendar based classes on an old version of Java will potentially give you incorrect data. Short of just updating your Java version all the time is there a way around this e.g. like an always up-to-date patch file you can put into you application?
Also, is the problem fixable for Applets in the same way - since your not directly in control of the Java version / update on which the application is run?
The best solution is to upgrade to the latest JRE and JDK.
If this is not possible for whatever reason, SUN makes a tool available for updating the timezone table to the latest one. You can find the TZUpdater here
The documentation of the Java TimeZone and derived SimpleTimeZone classes state that...
The class holds an offset from GMT, called raw offset, and start and end rules for a daylight saving time
schedule. Since it only holds single values for each, it cannot handle historical changes in the offset from
GMT and the daylight saving schedule, except that the setStartYear method can specify the year when the
daylight saving time schedule starts in effect.
This means that if your application does not rely on historical dates/times, you won't have issues if the TimeZone object you're using at the time that these objects are generated are up to date.
However...
If your application contains any sort of database of historical dates/times, then when a new Time Zone rule goes into effect, you application will erroneously interpret your historical information and display incorrect results since the TimeZone object it will be using is not the same as the TimeZone object that was used at the time your historical data was generated.
If it is important to you to maintain historically accurate dates/times (such as, for example, billing records to government agencies that have very sharply defined parameters for billing eligibility) then you'll need to store not only your historical date/time information in your database, but information that can be used to recreate the time zone rules that were in effect at that time as well. There are a few possible solutions.
The Java SimpleTimeZone class has a constructor that will allow you to create a TimeZone object with a single set of time zone and daylight savings rules:
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime,
int endMonth, int endDay, int endDayOfWeek, int endTime, int dstSavings)
Constructs a SimpleTimeZone with the given base time zone offset from GMT, time zone ID, and rules for
starting and ending the daylight time.
So, just make sure you store all the date/time information in your database as UTC timestamps. I like to store timestamps in databases as "Modified Julian Day Numbers" which are able to store calendar dates and times to millisecond precision in a double. Modified Julian Days numbers are always defined in UTC. Whatever though, as long as your info is in UTC, you're standard.
Then, you can create a table of all the arguments needed to create the SimpleTimeZone objects ever used in your database, or you can simply persist all the objects used by your database with the object persistence API of your liking. With a foreign key that indicates the time zone rules used for the relevant data, you can assure that your database application can report historically accurate local times.
Mark Rhodes is right if something at time zone was changed (like summertime on mine) it will return even wrong time. So end users should update their JREs.
PS: additionaly you can fine TimeZone Update Utility for IBM JRE here
http://www.ibm.com/developerworks/java/jdk/dst/jtzu.html
The java date reads out the time of the system. This time is normally sync with an timeserver, so, normally, there won't be problems.
The timezone API figures out daylight savings for you.
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/TimeZone.html
I've this url to set up the connection in my Italy website, however, when i try to perform some insert action from the site, the date is still not right. (it should be for example: 01:24, but it is 02:24)
jdbc.url=jdbc:mysql://sql.example.com/database?autoReconnect=true&characterEncoding=UTF-8&sessionVariables=time_zone='Europe/Rome'
Do I need to add any other params to make it work correctly?
Is there a complete list of all timezones?
Sorry I don't have the answer to your direct question. However I can suggest something worth considering that will avoid all time zone problems at the database entirely. If possible I recommend simply using BIGINT fields for storing dates with Java. You just store the long of the number of milliseconds since the epoch, e.g. from System.currentTimeMillis() or Date.getTime().
Then interpretation of the time zone for a date is always managed in Java, which is good at using the epoch based number. It does make it a little more involved to directly query the database for a date outside of Java, however it's not too hard and tends to be worth it IMO:
SELECT FROM_UNIXTIME(date_field / 1000) FROM table;
There is a list of "tz" timezone names in Wikipedia.