Cannot save Instant Max or Min in Database.
Description
Activity

Yoann RodièreJanuary 15, 2021 at 7:49 AM
Thanks for your answer, please keep us updated.
I think the way forward is indeed to modify applications so that they only try to store values that fall in the range of values accepted by the database. That will certainly be between LocalDateTime.MIN
and LocalDateTime.MAX
after conversion to the local timezone, which means ORM won’t throw an exception.
Maybe we could avoid the exception in Hibernate ORM, but even then, such extreme values would not be stored properly. I doubt many databases allow storing such extreme values as timestamps; from what I can see, they either raise an error (when you’re lucky) or silently use the closest value in the supported range.
In fact, if I was to change anything in Hibernate ORM, I would rather throw an exception in more cases, when we know values cannot be stored by the database. But I believe that’s the job of the JDBC driver, and would require passing the Instant
to the driver directly, instead of transforming it to a Timestamp. Which is a major change better suited for ORM 6.

Timm KorteJanuary 14, 2021 at 12:55 PM
After talking to the developers: They are not sure what they are getting back - as they used Instant.MAX
as a representation for “never expire” - and the cleanup code actually just looked for entries before a certain Instant (mostly “now”).
So now the code is changed to use Instant.ofEpochMilli(Long.MAX_VALUE)
(+292278994-08-17T07:12:55.807Z
- far enough to be “never” in the future and not fail on timezones).
Testing with existing datasets that have been stored with “Instant.MAX” will show if this is a valid path for us.

Yoann RodièreJanuary 13, 2021 at 9:55 AM
> what would be the effect when trying to retrieve entries from the database which were saved with Instant.MAX by a 5.3 version?
As far as I can see, it would correctly return
Instant.MAX
. The problem is with intermediary representations of the value when serializing, but deserialization code seems fine… at least forInstant.MAX
. It will probably fail to deserializeInstant.MIN
, though (same problem as serialization).
So, actually, Instant.MAX
and Instant.MIN
are never serialized correctly, even in 5.4.0. Instant
values are converted to Timestamp
before they are passed to the JDBC driver, and Timestamp
only supports dates/times ranging from 292278994-08-17 08:12:55.192
(BC) to 292278994-08-17 08:12:55.807
(AD), as far as I can tell. So they cannot represent Instant.MIN
and Instant.MAX
.
Even if we solved that, I don’t know of any database that supports storing such extreme values.
So… How have you been using this exactly? Are you sure you’re getting back Instant.MAX
/ Instant.MIN
when loading your entities from the database?

Yoann RodièreJanuary 13, 2021 at 8:40 AM
I’m looking into it. Strangely, it seems Instant.MAX
and Instant.MIN
are outside of the range of values that can be represented through LocalDateTime
, so the error only happens when converting the Instant
to a LocalDateTime
(we need the year/month/day/etc.) before we finally convert it to a Timestamp
.
what would be the effect when trying to retrieve entries from the database which were saved with Instant.MAX by a 5.3 version?
As far as I can see, it would correctly return Instant.MAX
. The problem is with intermediary representations of the value when serializing, but deserialization code seems fine… at least for Instant.MAX
. It will probably fail to deserialize Instant.MIN
, though (same problem as serialization).
Yes, it’s a mess. Converting between java.time
and Timestamp
requires to handle loads of corner cases. Hopefully it’ll get easier when we can rely on java.time
support in JDBC drivers and don’t need to perform a conversion anymore, but we cannot do that in 5.4 as it would mean dropping support for older JDBC drivers that were supported in 5.4.0.

Timm KorteJanuary 12, 2021 at 5:59 PM
@Yoann Rodière sorry for assigning - for some reason, I can’t mention here.
Testcase:
https://github.com/JavaTesting1/hibernate-test-case-templates/tree/InstantBug
Especially:
https://github.com/JavaTesting1/hibernate-test-case-templates/blob/InstantBug/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java
When trying to save Instant.MAX or Instant.MIN in database via entitymanager.persist the following errors occurr (ps my pc is in the german time zone):
For Instant.MAX:
java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): 1000000001
at java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330)
at java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:722)
at java.time.LocalDate.ofEpochDay(LocalDate.java:341)
at java.time.zone.ZoneRules.findYear(ZoneRules.java:928)
at java.time.zone.ZoneRules.getOffset(ZoneRules.java:487)
at java.time.ZonedDateTime.create(ZonedDateTime.java:455)
at java.time.ZonedDateTime.ofInstant(ZonedDateTime.java:409)
at java.time.Instant.atZone(Instant.java:1213)
at org.hibernate.type.descriptor.java.InstantJavaDescriptor.unwrap(InstantJavaDescriptor.java:70)
at org.hibernate.type.descriptor.java.InstantJavaDescriptor.unwrap(InstantJavaDescriptor.java:26)
at org.hibernate.type.descriptor.sql.TimestampTypeDescriptor$1.doBind(TimestampTypeDescriptor.java:48)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:74)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:39)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2875)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2850)
at org.hibernate.persister.entity.AbstractEntityPersister$4.bindValues(AbstractEntityPersister.java:3071)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:41)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3079)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3673)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:332)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:289)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:196)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:127)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:804)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.bugs.JPAUnitTestCase.testInstantMax(JPAUnitTestCase.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
For Instant.MIN:
java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): -1000000000
at java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330)
at java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:722)
at java.time.LocalDate.ofEpochDay(LocalDate.java:341)
at java.time.LocalDateTime.ofEpochSecond(LocalDateTime.java:422)
at java.time.ZonedDateTime.create(ZonedDateTime.java:456)
at java.time.ZonedDateTime.ofInstant(ZonedDateTime.java:409)
at java.time.Instant.atZone(Instant.java:1213)
at org.hibernate.type.descriptor.java.InstantJavaDescriptor.unwrap(InstantJavaDescriptor.java:70)
at org.hibernate.type.descriptor.java.InstantJavaDescriptor.unwrap(InstantJavaDescriptor.java:26)
at org.hibernate.type.descriptor.sql.TimestampTypeDescriptor$1.doBind(TimestampTypeDescriptor.java:48)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:74)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:39)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2875)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2850)
at org.hibernate.persister.entity.AbstractEntityPersister$4.bindValues(AbstractEntityPersister.java:3071)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:41)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3079)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3673)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:332)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:289)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:196)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:127)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:804)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.bugs.JPAUnitTestCase.testInstantMin(JPAUnitTestCase.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)