HHH-17229 Test and fix for handling negative milliseconds from java.sql.Time

This commit is contained in:
Christian Beikov 2024-05-06 14:05:00 +02:00
parent b21d70ef74
commit a81fae743c
6 changed files with 163 additions and 3 deletions

View File

@ -133,10 +133,15 @@ public class JdbcTimeJavaType extends AbstractTemporalJavaType<Date> {
? ( (java.sql.Time) value )
: new java.sql.Time( value.getTime() % 86_400_000 );
final LocalTime localTime = time.toLocalTime();
final long millis = time.getTime() % 1000;
long millis = time.getTime() % 1000;
if ( millis == 0 ) {
return localTime;
}
if ( millis < 0 ) {
// The milliseconds for a Time could be negative,
// which usually means the time is in a different time zone
millis += 1_000L;
}
return localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
}

View File

@ -136,10 +136,15 @@ public class LocalTimeJavaType extends AbstractTemporalJavaType<LocalTime> {
if (value instanceof Time) {
final Time time = (Time) value;
final LocalTime localTime = time.toLocalTime();
final long millis = time.getTime() % 1000;
long millis = time.getTime() % 1000;
if ( millis == 0 ) {
return localTime;
}
if ( millis < 0 ) {
// The milliseconds for a Time could be negative,
// which usually means the time is in a different time zone
millis += 1_000L;
}
return localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
}

View File

@ -186,10 +186,15 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
final OffsetTime offsetTime = time.toLocalTime()
.atOffset( getCurrentJdbcOffset( options) )
.withOffsetSameInstant( getCurrentSystemOffset() );
final long millis = time.getTime() % 1000;
long millis = time.getTime() % 1000;
if ( millis == 0 ) {
return offsetTime;
}
if ( millis < 0 ) {
// The milliseconds for a Time could be negative,
// which usually means the time is in a different time zone
millis += 1_000L;
}
return offsetTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
}

View File

@ -0,0 +1,36 @@
package org.hibernate.orm.test.type.descriptor.java;
import java.sql.Time;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.Date;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
import org.hibernate.type.descriptor.java.LocalTimeJavaType;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@BaseUnitTest
public class JdbcTimeJavaTypeDescriptorTest {
@Test
@JiraKey("HHH-17229")
public void testUnwrap() {
final JavaType<Date> javaType = JdbcTimeJavaType.INSTANCE;
final Time sqlTime = new Time(
LocalDate.EPOCH.atTime( LocalTime.of( 0, 1, 2, 0 ) )
.toInstant( ZoneOffset.ofHours( 4 ) )
.plusMillis( 123 )
.toEpochMilli()
);
final LocalTime wrappedSqlTime = javaType.unwrap( sqlTime, LocalTime.class, null );
assertThat( wrappedSqlTime ).isEqualTo( LocalTime.of( 21, 1, 2, 123_000_000 ) );
}
}

View File

@ -0,0 +1,33 @@
package org.hibernate.orm.test.type.descriptor.java;
import java.sql.Time;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneOffset;
import org.hibernate.type.descriptor.java.LocalTimeJavaType;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@BaseUnitTest
public class LocalTimeJavaTypeDescriptorTest {
@Test
@JiraKey("HHH-17229")
public void testWrap() {
final LocalTimeJavaType javaType = LocalTimeJavaType.INSTANCE;
final Time sqlTime = new Time(
LocalDate.EPOCH.atTime( LocalTime.of( 0, 1, 2, 0 ) )
.toInstant( ZoneOffset.ofHours( 4 ) )
.plusMillis( 123 )
.toEpochMilli()
);
final LocalTime wrappedSqlTime = javaType.wrap( sqlTime, null );
assertThat( wrappedSqlTime ).isEqualTo( LocalTime.of( 21, 1, 2, 123_000_000 ) );
}
}

View File

@ -0,0 +1,76 @@
package org.hibernate.orm.test.type.descriptor.java;
import java.sql.Time;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneOffset;
import java.util.TimeZone;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.LocalTimeJavaType;
import org.hibernate.type.descriptor.java.OffsetTimeJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@BaseUnitTest
public class OffsetTimeJavaTypeDescriptorTest {
@Test
@JiraKey("HHH-17229")
public void testWrap() {
final OffsetTimeJavaType javaType = OffsetTimeJavaType.INSTANCE;
final WrapperOptions wrapperOptions = new WrapperOptions() {
@Override
public SharedSessionContractImplementor getSession() {
return null;
}
@Override
public SessionFactoryImplementor getSessionFactory() {
return null;
}
public boolean useStreamForLobBinding() {
return false;
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return 0;
}
public LobCreator getLobCreator() {
return NonContextualLobCreator.INSTANCE;
}
public JdbcType remapSqlTypeDescriptor(JdbcType sqlTypeDescriptor) {
return sqlTypeDescriptor;
}
@Override
public TimeZone getJdbcTimeZone() {
return null;
}
};
final Time sqlTime = new Time(
LocalDate.EPOCH.atTime( LocalTime.of( 0, 1, 2, 0 ) )
.toInstant( ZoneOffset.ofHours( 4 ) )
.plusMillis( 123 )
.toEpochMilli()
);
final OffsetTime wrappedSqlTime = javaType.wrap( sqlTime, wrapperOptions );
assertThat( wrappedSqlTime ).isEqualTo( LocalTime.of( 21, 1, 2, 123_000_000 ).atOffset( OffsetDateTime.now().getOffset() ) );
}
}