Reading DATE field from Oracle with JDBC - java

I'm using an Oracle server, which I don't control, in another time zone (US, Central). There is a table with a DATE column, which gets a default value of SYSDATE when things are inserted (I.e., the column is always set with the system's time, without the programmer needing to set it explicitly).
I'm in a different time zone (US, East Coast). How do I read this column with JDBC and get an Epoch/UTC time?
When I call getTimestamp().getTime() (on the ResultSet), I get the time converted to local time. That is, if something was inserted at 10 am on the server, it is 11 am here (and 4 pm in UTC). When I call getTimestamp().getTime() I get an epoch of 10 am local time (which is 3 pm UTC).
I have a partial answer.
Calendar sqlServerCalendar = Calendar.getInstance(TimeZone.getTimeZone("CST"));
long t = rs.getTimestamp("timestampcolumn", sqlServerCalendar).getTime();
This is not ideal, because I have to specify the server's time zone (which I'd move into settings). But it does seem to return the answer I want. Is there a way I can query the server's time zone at run-time, rather than needing a setting? Or, is there a better way to do this? I would prefer a solution to not have to know (via hard-coded values, or settings) which time zone it or the server are in (so it will work unmodified if run in different time zones, and/or if the server moves).

You can pass the JVM parameter -Duser.timezone such as java -Duser.timezone=GMT and that should do the trick. But again one important thing to keep in mind about setting the timezone for the entire JVM is that it affects everything.
If you just need to set only for time zones when retrieving date, time or timestamp objects. you can do it like
Calendar mycal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
then on the result set
Date correctDate = resultset.getDate("Timestampcolumn",mycal);
I believe you should also be able to set the timezone using TimeZone.setDefault() as well.

Related

UTC Unix timestamp storage in HSQLDB from Java program in wrong timezone

I am trying to understand how to store an Unix timestamp (that is, a number of seconds since 1970 expressed in the UTC/GMT timezone) inside an HSQLDB embedded file database. However, I have yet to understand how TZ handling works with HSQL.
My program will be used from different zones, so using UTC is a must. Moreover, I cannot change the default time zone (as with java.util.TimeZone.setDefault) as it will be embedded in other programs and therefore should not change the environment.
My attempt - the doc states:
When datetime values are sent to the database using the PreparedStatement or CallableStatement interfaces, the Java object is converted to the type of the prepared or callable statement parameter. This type may be DATE, TIME, or TIMESTAMP (with or without time zone). The time zone displacement is the time zone of the JDBC session.
So I use a TIMESTAMP column inside the database (without time zone - the default), and issue SET TIME ZONE INTERVAL '0:00' HOUR TO MINUTE (to put the session in UTC TZ) and then INSERT INTO TEST VALUES(?) with ? being a Java Timestamp object containing the correct Unix value (GMT related, tested OK).
Sadly, in this case, the SQL log of the database shows the timestamp has been reverted back to my local time zone (+2). For a timestamp of 1442132237635 (8H17 in UTC, 10H17 in +2), we get TIMESTAMP'2015-09-13 10:17:17.602000' in the log. Wrong result... it seems changing the session time zone has absolutely no impact whatsoever (I've tried +14, -14... no changes). The SET order is however correctly executed - it appears in the SQL log, and the value of TIMEZONE() changes afterwards.
Other attempts
I've also tried to use a TIMESTAMP WITH TIME ZONE column, without setting the session TZ. In that case, the database stores 'local time +2', and I can extract the correct timestamp from it. This is borderline ridiculous - it means the HSQLDB driver takes the Java timestamp (UTC), interprets it correctly as UTC, casts it to the JVM default TZ then sends that to the DB. I don't want the TZ info in the DB - don't need it. (of note: changing the session TZ has no impact - the SQL order issued is always with my local TZ... makes you wonder what is the point of the SET TZ order)
And changing the default JVM TZ to UTC works but as stated above I cannot do that.
Also of note: this question seems related but the provided answer is basically to hack every SQL order which i would like to avoid...
My question
How can I simply store my UTC Java timestamp inside HSQLDB? And what is the purpose of the SET TIME ZONE order?
Thanks for reading.
Your first attempt is correct and will return the correct results.
Your database is located in UTC+2 time zone. You are connecting as if you were a remote user in the UTC time zone.
The log storage of the timestamp is not wrong as you claim. Log storage is not for the user of the database to read. The database stores its log in your location's time zone. Next time it reads the log it reads and interprets it according to your database time zone (nothing to do with the session time zone). Therefore the time stored in log as 10:17 will be 8:17 in UTC.
With TIMESTAMP WITHOUT TIME ZONE, the database files cannot be moved to a different time zone, as the timestamps will be interpreted according to the new location's zone.
The role of the SET TIME ZONE statement is to allow the correct adjustment when the data is stored. It does not change previously stored data. Naturally if a time is stored as 8:17, it should be read as 8:17 whenever and in whichever zone it is accessed.
For best portability use TIMESTAMP WITH TIME ZONE. The way it stores and retrieves the data is correct even if you move the database files.

Java, Hibernate, MySQL - store UTC date-time

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.

Should I store the timezone separately from the timestamp for Postgres and JDBC?

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.

How do you keep TimeZone and Calendar up to date after a time zone change?

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

Timezone issues using java.util.Date while writing into Oracle

Setup
Web application runs on machine A with system time set to GMT
Oracle database runs on machine B with system time set to GMT. (select dbtimezone from dual shows +0:00)
Problem
Web application uses Hades JPA and Hibernate to write and read from the DB. A table has two columns of type timestamp called CREATED and MODIFIED. The web applications uses joda DateTime to get java.util.Date object and writes it into the DB. Things are fine the first time the entry is created, both fields have time set in GMT. But once this same row gets updated the MODIFIED column gets set in PST.
It is the same code section that gets executed to set date values during creation as well as during modification (It tries getting java.util.Date from joda DateTime as mentioned above).
Can someone explain this behavior?
One more thing I have noticed is in the above setup if in the web application I set the date column using new Date(), it sets it in PST. I am clueless as where is it getting the timezone as PST when the application and DB are both running in machines set to GMT.
EDIT: TimeZone.getDefault() prints GMT in both the machines.
Check what timezone the JVM thinks it's running in by calling java.util.TimeZone.getDefault(). That would be the timezone it's applying to the Date when it's read from your jdbc driver. Although I've used hibernate, I'm not sure what configurability it has on the timezone. Nevertheless, I'd say under the hood it's calling java.sql.ResultSet.getDate(). This function has an overload getDate(String columnLabel, Calendar cal) which allows you to specify the timezone of the column it's reading from the database. That's probably the root cause of the problem. If Hibernate is calling the default though getDate(String columnLabel) then more than likely the problem is the timezone your JVM thinks it's in is not GMT.

Categories

Resources