提交 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
return type.cast(LocalDateTimeUtils.valueToInstant(value));
} else if (type == LocalDateTimeUtils.OFFSET_DATE_TIME) {
return type.cast(LocalDateTimeUtils.valueToOffsetDateTime(value));
} else if (type == LocalDateTimeUtils.DURATION) {
return type.cast(LocalDateTimeUtils.valueToDuration(value));
} else {
throw unsupported(type.getName());
}
......
......@@ -9,12 +9,17 @@ package org.h2.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueInterval;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
......@@ -65,6 +70,11 @@ public class LocalDateTimeUtils {
*/
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}.
*/
......@@ -146,6 +156,21 @@ public class LocalDateTimeUtils {
*/
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;
static {
......@@ -155,9 +180,10 @@ public class LocalDateTimeUtils {
INSTANT = tryGetClass("java.time.Instant");
OFFSET_DATE_TIME = tryGetClass("java.time.OffsetDateTime");
ZONE_OFFSET = tryGetClass("java.time.ZoneOffset");
DURATION = tryGetClass("java.time.Duration");
IS_JAVA8_DATE_API_PRESENT = LOCAL_DATE != null && LOCAL_TIME != 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) {
LOCAL_TIME_OF_NANO = getMethod(LOCAL_TIME, "ofNanoOfDay", long.class);
......@@ -187,6 +213,10 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME, "of", LOCAL_DATE_TIME, ZONE_OFFSET);
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 {
LOCAL_TIME_OF_NANO = null;
LOCAL_TIME_TO_NANO = null;
......@@ -206,6 +236,9 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME_GET_OFFSET = null;
OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET = 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 {
}
}
/**
* 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.
*
......@@ -493,4 +552,26 @@ public class LocalDateTimeUtils {
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 {
} else if (x instanceof Interval) {
Interval i = (Interval) x;
return ValueInterval.from(i.getQualifier(), i.getLeading(), i.getRemaining());
} else if (clazz == LocalDateTimeUtils.DURATION) {
return LocalDateTimeUtils.durationToValue(x);
} else {
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getValue(type, x,
......
......@@ -183,6 +183,7 @@ public class TestPreparedStatement extends TestDb {
testOffsetDateTime8(conn);
testInstant8(conn);
testInterval(conn);
testInterval8(conn);
testArray(conn);
testSetObject(conn);
testPreparedSubquery(conn);
......@@ -916,6 +917,25 @@ public class TestPreparedStatement extends TestDb {
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 {
Statement s = conn.createStatement();
s.executeUpdate("CREATE TABLE TEST(ID IDENTITY, FLAG BIT)");
......
......@@ -106,6 +106,7 @@ public class TestResultSet extends TestDb {
testDatetime();
testDatetimeWithCalendar();
testInterval();
testInterval8();
testBlob();
testClob();
testAutoIncrement();
......@@ -1581,6 +1582,30 @@ public class TestResultSet extends TestDb {
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 {
trace("Test BLOB");
ResultSet rs;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论