I created a table like like this in PostgreSQL:
create table myTable (
dateAdded timestamp(0) without time zone null
The solution proposed by Pavel (adding the jvm param '-Duser.timezone=UTC') is for sure the best option, if you don't have system access, this isn't always possible to do.
The only way I found is to convert the timestamp to epoch in the query and read it as a long
.
SELECT extract(epoch from my_timestamp_colum at time zone 'utc')::bigint * 1000
AS my_timestamp_as_epoc
FROM my_table
Then read it in plain JDBC with
ResultSet rs = ...
long myTimestampAsEpoc = rs.getLong("my_timestamp_as_epoc");
Date myTimestamp = new Date(myTimestampAsEpoc);
With iBatis/MyBatis, you need to handle the column as a Long, then convert it manually to Date/Timestamp. Inconvenient and ugly.
The reverse operation in PostgreSQL can be done with:
SELECT TIMESTAMP WITHOUT TIME ZONE 'epoch' +
1421855729000 * INTERVAL '1 millisecond'
That said, the JDBC specification doesn't say that the returned timestamp shall or shall not shift the timestamp to the user time zone; BUT since you defined the table column as 'timestamp without time zone' I would expect no time shifts, but the recorded epoch being just wrapped in a java.sql.Timestamp
. For my opinion, this is a bug in the PostgreSQL JDBC driver. Being the problem at this level, then, probably there is not much more that can be done, without system access.
There are a few tricks specific for the Postgres JDBC driver
See https://jdbc.postgresql.org/documentation/head/java8-date-time.html
So when reading you can do
Instant utc =resultSet.getObject("dateAdded",LocalDateTime.class).toInstant(ZoneOffset.UTC);
If you use a connection pool such as Hikari, you can also specify the time time-zone used by each connection by setting connectionInitSql=set time zone 'UTC'
Set UTC as default timezone of your JVM -Duser.timezone=UTC
or set your whole OS to UTC.
In Postgres both TIMESTAMP
and TIMESTAMP WITH TIMEZONE
are stored the same way - number of seconds since Postgres epoch (2000-01-01). The main difference is what Postgres do when it saves timestamp value such as 2004-10-19 10:23:54+02
:
+02
is just stripped away -02
correction is performed to make it UTC Now the interesting thing is when JDBC driver loads the value:
In both cases you will end up with java.sql.Timestamp
object with user's default TZ.
Timestamps without TZ are pretty limited. If you have two systems connected to your database, both with different TZ, they will interpret timestamps differently. Therefore, I strongly advice you to use TIMESTAMP WITH TIMEZONE
.
You can tell JDBC what kind of TZ it should use when reading timestamp via ResultSet#getTimestamp(String, Calendar). Excerpt from JavaDoc:
This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does not store timezone information.