JadiraOrg/jadira

LocalDateTime gets mapped to a concrete time zone (PostgreSQL)

Closed this issue · 4 comments

In my app I have timestamps corresponding to multiple timezones.
Due to historical reasons, they are all stored in a PostgreSQL TIMESTAMP WITHOUT TIMEZONE column, and the actual timezone is stored somewhere else.

Because of this, I've decided to use the Joda LocalDateTime type for these objects - not to worry about the actual timezone (besides, whatever is stored in the DB is usually what the user wants to see).

But now I've started using Jadira 3.2 which internally creates a Joda DateTime object out of the LocalDateTime object and fails when it hits the DST gap in the server timezone:

org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-29T02:30:00.000 (Europe/Berlin)
at org.joda.time.chrono.ZonedChronology.localToUTC(ZonedChronology.java:143)
at org.joda.time.chrono.ZonedChronology.getDateTimeMillis(ZonedChronology.java:118)
at org.joda.time.chrono.AssembledChronology.getDateTimeMillis(AssembledChronology.java:133)
at org.joda.time.base.BaseDateTime.<init>(BaseDateTime.java:258)
at org.joda.time.DateTime.<init>(DateTime.java:532)
at org.joda.time.LocalDateTime.toDateTime(LocalDateTime.java:750)
at org.joda.time.LocalDateTime.toDateTime(LocalDateTime.java:731)
at org.jadira.usertype.dateandtime.joda.columnmapper.TimestampColumnLocalDateTimeMapper.toNonNullValue(TimestampColumnLocalDateTimeMapper.java:64)
at org.jadira.usertype.dateandtime.joda.columnmapper.TimestampColumnLocalDateTimeMapper.toNonNullValue(TimestampColumnLocalDateTimeMapper.java:29)
at org.jadira.usertype.spi.shared.AbstractSingleColumnUserType.nullSafeSet(AbstractSingleColumnUserType.java:100)
at org.hibernate.type.CustomType.nullSafeSet(CustomType.java:158)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2843)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3121)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)

Since both my DB column (TIMESTAMP WITHOUT TIMEZONE) and my Java object (LocalDateTime) are timezone-agnostic I would expect the corresponding Jadira mapping to also be timezone-agnostic.

It _looks_ to me like an issue in Jadira. Could you suggest some workaround I could apply to get through this problem right away?

Thank you very much in advance.

Any tips / feedback? Please...

I think it could be possible to support this via a new usertype. JDBC itself doesn't have built in support for this type, you would need to see if you can map it e.g. via PGObject. Have a look at this (resolved) issue which maps an enumerated type for Postgres in this way #14

If you are interested to work on this, I am happy to assist with integrating it into the library

The next build will remove databaseZone in favour of using the Hibernate parameter. You'll still be able to set Java Zone in some cases. This means this issue should be resolved

I've reinstated databaseZone, but it now delegates to Hibernate to handle (by proxying the resolution of the TimeZone from the session). This means it overrides the Hibernate property, but it also means the database zone must be specified using JDK TimeZone format in all cases.