提交 e17bac80 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add support for java.time.Duration

上级 3f900196
...@@ -3949,6 +3949,8 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS ...@@ -3949,6 +3949,8 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
return type.cast(LocalDateTimeUtils.valueToInstant(value)); return type.cast(LocalDateTimeUtils.valueToInstant(value));
} else if (type == LocalDateTimeUtils.OFFSET_DATE_TIME) { } else if (type == LocalDateTimeUtils.OFFSET_DATE_TIME) {
return type.cast(LocalDateTimeUtils.valueToOffsetDateTime(value)); return type.cast(LocalDateTimeUtils.valueToOffsetDateTime(value));
} else if (type == LocalDateTimeUtils.DURATION) {
return type.cast(LocalDateTimeUtils.valueToDuration(value));
} else { } else {
throw unsupported(type.getName()); throw unsupported(type.getName());
} }
......
...@@ -9,12 +9,17 @@ package org.h2.util; ...@@ -9,12 +9,17 @@ package org.h2.util;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigInteger;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueInterval;
import org.h2.value.ValueTime; import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp; import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone; import org.h2.value.ValueTimestampTimeZone;
...@@ -65,6 +70,11 @@ public class LocalDateTimeUtils { ...@@ -65,6 +70,11 @@ public class LocalDateTimeUtils {
*/ */
private static final Class<?> ZONE_OFFSET; private static final Class<?> ZONE_OFFSET;
/**
* {@code Class<java.time.Duration>} or {@code null}.
*/
public static final Class<?> DURATION;
/** /**
* {@code java.time.LocalTime#ofNanoOfDay()} or {@code null}. * {@code java.time.LocalTime#ofNanoOfDay()} or {@code null}.
*/ */
...@@ -146,6 +156,21 @@ public class LocalDateTimeUtils { ...@@ -146,6 +156,21 @@ public class LocalDateTimeUtils {
*/ */
private static final Method ZONE_OFFSET_GET_TOTAL_SECONDS; private static final Method ZONE_OFFSET_GET_TOTAL_SECONDS;
/**
* {@code java.time.Duration#ofSeconds(long, long)} or {@code null}.
*/
private static final Method DURATION_OF_SECONDS;
/**
* {@code java.time.Duration#getSeconds()} or {@code null}.
*/
private static final Method DURATION_GET_SECONDS;
/**
* {@code java.time.Duration#getNano()} or {@code null}.
*/
private static final Method DURATION_GET_NANO;
private static final boolean IS_JAVA8_DATE_API_PRESENT; private static final boolean IS_JAVA8_DATE_API_PRESENT;
static { static {
...@@ -155,9 +180,10 @@ public class LocalDateTimeUtils { ...@@ -155,9 +180,10 @@ public class LocalDateTimeUtils {
INSTANT = tryGetClass("java.time.Instant"); INSTANT = tryGetClass("java.time.Instant");
OFFSET_DATE_TIME = tryGetClass("java.time.OffsetDateTime"); OFFSET_DATE_TIME = tryGetClass("java.time.OffsetDateTime");
ZONE_OFFSET = tryGetClass("java.time.ZoneOffset"); ZONE_OFFSET = tryGetClass("java.time.ZoneOffset");
DURATION = tryGetClass("java.time.Duration");
IS_JAVA8_DATE_API_PRESENT = LOCAL_DATE != null && LOCAL_TIME != null && IS_JAVA8_DATE_API_PRESENT = LOCAL_DATE != null && LOCAL_TIME != null &&
LOCAL_DATE_TIME != null && INSTANT != null && LOCAL_DATE_TIME != null && INSTANT != null &&
OFFSET_DATE_TIME != null && ZONE_OFFSET != null; OFFSET_DATE_TIME != null && ZONE_OFFSET != null && DURATION != null;
if (IS_JAVA8_DATE_API_PRESENT) { if (IS_JAVA8_DATE_API_PRESENT) {
LOCAL_TIME_OF_NANO = getMethod(LOCAL_TIME, "ofNanoOfDay", long.class); LOCAL_TIME_OF_NANO = getMethod(LOCAL_TIME, "ofNanoOfDay", long.class);
...@@ -187,6 +213,10 @@ public class LocalDateTimeUtils { ...@@ -187,6 +213,10 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME, "of", LOCAL_DATE_TIME, ZONE_OFFSET); OFFSET_DATE_TIME, "of", LOCAL_DATE_TIME, ZONE_OFFSET);
ZONE_OFFSET_GET_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "getTotalSeconds"); ZONE_OFFSET_GET_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "getTotalSeconds");
DURATION_OF_SECONDS = getMethod(DURATION, "ofSeconds", long.class, long.class);
DURATION_GET_SECONDS = getMethod(DURATION, "getSeconds");
DURATION_GET_NANO = getMethod(DURATION, "getNano");
} else { } else {
LOCAL_TIME_OF_NANO = null; LOCAL_TIME_OF_NANO = null;
LOCAL_TIME_TO_NANO = null; LOCAL_TIME_TO_NANO = null;
...@@ -206,6 +236,9 @@ public class LocalDateTimeUtils { ...@@ -206,6 +236,9 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME_GET_OFFSET = null; OFFSET_DATE_TIME_GET_OFFSET = null;
OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET = null; OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET = null;
ZONE_OFFSET_GET_TOTAL_SECONDS = null; ZONE_OFFSET_GET_TOTAL_SECONDS = null;
DURATION_OF_SECONDS = null;
DURATION_GET_SECONDS = null;
DURATION_GET_NANO = null;
} }
} }
...@@ -349,6 +382,32 @@ public class LocalDateTimeUtils { ...@@ -349,6 +382,32 @@ public class LocalDateTimeUtils {
} }
} }
/**
* Converts a value to a OffsetDateTime.
*
* <p>This method should only called from Java 8 or later.</p>
*
* @param value the value to convert
* @return the OffsetDateTime
*/
public static Object valueToDuration(Value value) {
if (!(value instanceof ValueInterval)) {
value = value.convertTo(Value.INTERVAL_DAY_TO_SECOND);
}
if (DataType.isYearMonthIntervalType(value.getType())) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, (Throwable) null, value.getString());
}
BigInteger[] dr = DateTimeUtils.intervalToAbsolute((ValueInterval) value)
.divideAndRemainder(BigInteger.valueOf(1_000_000_000));
try {
return DURATION_OF_SECONDS.invoke(null, dr[0].longValue(), dr[1].longValue());
} catch (IllegalAccessException e) {
throw DbException.convert(e);
} catch (InvocationTargetException e) {
throw DbException.convertInvocation(e, "timestamp with time zone conversion failed");
}
}
/** /**
* Converts a LocalDate to a Value. * Converts a LocalDate to a Value.
* *
...@@ -493,4 +552,26 @@ public class LocalDateTimeUtils { ...@@ -493,4 +552,26 @@ public class LocalDateTimeUtils {
return LOCAL_DATE_TIME_PLUS_NANOS.invoke(localDateTime, timeNanos); return LOCAL_DATE_TIME_PLUS_NANOS.invoke(localDateTime, timeNanos);
} }
/**
* Converts a Duration to a Value.
*
* @param duration the Duration to convert, not {@code null}
* @return the value
*/
public static ValueInterval durationToValue(Object duration) {
try {
long seconds = (long) DURATION_GET_SECONDS.invoke(duration);
int nano = (int) DURATION_GET_NANO.invoke(duration);
if (seconds < 0 && nano != 0) {
nano = 1_000_000_000 - nano;
seconds++;
}
return ValueInterval.from(IntervalQualifier.SECOND, seconds, nano);
} catch (IllegalAccessException e) {
throw DbException.convert(e);
} catch (InvocationTargetException e) {
throw DbException.convertInvocation(e, "time conversion failed");
}
}
} }
...@@ -1275,6 +1275,8 @@ public class DataType { ...@@ -1275,6 +1275,8 @@ public class DataType {
} else if (x instanceof Interval) { } else if (x instanceof Interval) {
Interval i = (Interval) x; Interval i = (Interval) x;
return ValueInterval.from(i.getQualifier(), i.getLeading(), i.getRemaining()); return ValueInterval.from(i.getQualifier(), i.getLeading(), i.getRemaining());
} else if (clazz == LocalDateTimeUtils.DURATION) {
return LocalDateTimeUtils.durationToValue(x);
} else { } else {
if (JdbcUtils.customDataTypesHandler != null) { if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getValue(type, x, return JdbcUtils.customDataTypesHandler.getValue(type, x,
......
...@@ -183,6 +183,7 @@ public class TestPreparedStatement extends TestDb { ...@@ -183,6 +183,7 @@ public class TestPreparedStatement extends TestDb {
testOffsetDateTime8(conn); testOffsetDateTime8(conn);
testInstant8(conn); testInstant8(conn);
testInterval(conn); testInterval(conn);
testInterval8(conn);
testArray(conn); testArray(conn);
testSetObject(conn); testSetObject(conn);
testPreparedSubquery(conn); testPreparedSubquery(conn);
...@@ -916,6 +917,25 @@ public class TestPreparedStatement extends TestDb { ...@@ -916,6 +917,25 @@ public class TestPreparedStatement extends TestDb {
assertEquals(interval, rs.getObject(1, Interval.class)); assertEquals(interval, rs.getObject(1, Interval.class));
} }
private void testInterval8(Connection conn) throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
PreparedStatement prep = conn.prepareStatement("SELECT ?");
Object duration;
try {
duration = LocalDateTimeUtils.DURATION.getMethod("ofSeconds", long.class, long.class)
.invoke(null, -4, 900_000_000);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
prep.setObject(1, duration);
ResultSet rs = prep.executeQuery();
rs.next();
assertEquals("INTERVAL '-3.1' SECOND", rs.getString(1));
assertEquals(duration, rs.getObject(1, LocalDateTimeUtils.DURATION));
}
private void testPreparedSubquery(Connection conn) throws SQLException { private void testPreparedSubquery(Connection conn) throws SQLException {
Statement s = conn.createStatement(); Statement s = conn.createStatement();
s.executeUpdate("CREATE TABLE TEST(ID IDENTITY, FLAG BIT)"); s.executeUpdate("CREATE TABLE TEST(ID IDENTITY, FLAG BIT)");
......
...@@ -106,6 +106,7 @@ public class TestResultSet extends TestDb { ...@@ -106,6 +106,7 @@ public class TestResultSet extends TestDb {
testDatetime(); testDatetime();
testDatetimeWithCalendar(); testDatetimeWithCalendar();
testInterval(); testInterval();
testInterval8();
testBlob(); testBlob();
testClob(); testClob();
testAutoIncrement(); testAutoIncrement();
...@@ -1581,6 +1582,30 @@ public class TestResultSet extends TestDb { ...@@ -1581,6 +1582,30 @@ public class TestResultSet extends TestDb {
assertEquals(Interval.class.getName(), metaData.getColumnClassName(1)); assertEquals(Interval.class.getName(), metaData.getColumnClassName(1));
} }
private void testInterval8() throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
trace("Test INTERVAL 8");
ResultSet rs;
rs = stat.executeQuery("CALL INTERVAL '-3.1' SECOND");
rs.next();
assertEquals("INTERVAL '-3.1' SECOND", rs.getString(1));
Object expected;
try {
expected = LocalDateTimeUtils.DURATION.getMethod("ofSeconds", long.class, long.class)
.invoke(null, -4, 900_000_000);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
assertEquals(expected, rs.getObject(1, LocalDateTimeUtils.DURATION));
rs = stat.executeQuery("CALL INTERVAL '1-2' YEAR TO MONTH");
rs.next();
assertThrows(ErrorCode.DATA_CONVERSION_ERROR_1, rs).getObject(1, LocalDateTimeUtils.DURATION);
}
private void testBlob() throws SQLException { private void testBlob() throws SQLException {
trace("Test BLOB"); trace("Test BLOB");
ResultSet rs; ResultSet rs;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论