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

Fix subtraction of timestamps

上级 5b87daab
...@@ -5,6 +5,11 @@ ...@@ -5,6 +5,11 @@
*/ */
package org.h2.expression; package org.h2.expression;
import static org.h2.util.DateTimeUtils.NANOS_PER_DAY;
import static org.h2.util.DateTimeUtils.absoluteDayFromDateValue;
import static org.h2.util.DateTimeUtils.dateAndTimeFromValue;
import static org.h2.util.DateTimeUtils.dateTimeToValue;
import static org.h2.util.DateTimeUtils.dateValueFromAbsoluteDay;
import static org.h2.util.IntervalUtils.NANOS_PER_DAY_BI; import static org.h2.util.IntervalUtils.NANOS_PER_DAY_BI;
import java.math.BigDecimal; import java.math.BigDecimal;
...@@ -17,7 +22,6 @@ import org.h2.message.DbException; ...@@ -17,7 +22,6 @@ import org.h2.message.DbException;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.DateTimeFunctions; import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils; import org.h2.util.IntervalUtils;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -73,6 +77,12 @@ public class IntervalOperation extends Expression { ...@@ -73,6 +77,12 @@ public class IntervalOperation extends Expression {
private Expression left, right; private Expression left, right;
private int dataType; private int dataType;
private static BigInteger nanosFromValue(Value v) {
long[] a = dateAndTimeFromValue(v);
return BigInteger.valueOf(absoluteDayFromDateValue(a[0])).multiply(NANOS_PER_DAY_BI)
.add(BigInteger.valueOf(a[1]));
}
public IntervalOperation(IntervalOpType opType, Expression left, Expression right) { public IntervalOperation(IntervalOpType opType, Expression left, Expression right) {
this.opType = opType; this.opType = opType;
this.left = left; this.left = left;
...@@ -161,21 +171,15 @@ public class IntervalOperation extends Expression { ...@@ -161,21 +171,15 @@ public class IntervalOperation extends Expression {
return ValueInterval.from(IntervalQualifier.HOUR_TO_SECOND, negative, diff / 3_600_000_000_000L, return ValueInterval.from(IntervalQualifier.HOUR_TO_SECOND, negative, diff / 3_600_000_000_000L,
diff % 3_600_000_000_000L); diff % 3_600_000_000_000L);
} else if (lType == Value.DATE && rType == Value.DATE) { } else if (lType == Value.DATE && rType == Value.DATE) {
long diff = DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue()) long diff = absoluteDayFromDateValue(((ValueDate) l).getDateValue())
- DateTimeUtils.absoluteDayFromDateValue(((ValueDate) r).getDateValue()); - absoluteDayFromDateValue(((ValueDate) r).getDateValue());
boolean negative = diff < 0; boolean negative = diff < 0;
if (negative) { if (negative) {
diff = -diff; diff = -diff;
} }
return ValueInterval.from(IntervalQualifier.DAY, negative, diff, 0L); return ValueInterval.from(IntervalQualifier.DAY, negative, diff, 0L);
} else { } else {
long[] a = DateTimeUtils.dateAndTimeFromValue(l); BigInteger diff = nanosFromValue(l).subtract(nanosFromValue(r));
long[] b = DateTimeUtils.dateAndTimeFromValue(r);
BigInteger bi1 = BigInteger.valueOf(a[0]).multiply(NANOS_PER_DAY_BI)
.add(BigInteger.valueOf(a[1]));
BigInteger bi2 = BigInteger.valueOf(b[0]).multiply(NANOS_PER_DAY_BI)
.add(BigInteger.valueOf(b[1]));
BigInteger diff = bi1.subtract(bi2);
if (lType == Value.TIMESTAMP_TZ || rType == Value.TIMESTAMP_TZ) { if (lType == Value.TIMESTAMP_TZ || rType == Value.TIMESTAMP_TZ) {
l = l.convertTo(Value.TIMESTAMP_TZ); l = l.convertTo(Value.TIMESTAMP_TZ);
r = r.convertTo(Value.TIMESTAMP_TZ); r = r.convertTo(Value.TIMESTAMP_TZ);
...@@ -214,14 +218,13 @@ public class IntervalOperation extends Expression { ...@@ -214,14 +218,13 @@ public class IntervalOperation extends Expression {
} else { } else {
BigInteger a2 = IntervalUtils.intervalToAbsolute((ValueInterval) r); BigInteger a2 = IntervalUtils.intervalToAbsolute((ValueInterval) r);
if (lType == Value.DATE) { if (lType == Value.DATE) {
BigInteger a1 = BigInteger BigInteger a1 = BigInteger.valueOf(absoluteDayFromDateValue(((ValueDate) l).getDateValue()));
.valueOf(DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue()));
a2 = a2.divide(NANOS_PER_DAY_BI); a2 = a2.divide(NANOS_PER_DAY_BI);
BigInteger n = opType == IntervalOpType.DATETIME_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2); BigInteger n = opType == IntervalOpType.DATETIME_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2);
return ValueDate.fromDateValue(DateTimeUtils.dateValueFromAbsoluteDay(n.longValue())); return ValueDate.fromDateValue(dateValueFromAbsoluteDay(n.longValue()));
} else { } else {
long[] a = DateTimeUtils.dateAndTimeFromValue(l); long[] a = dateAndTimeFromValue(l);
long absoluteDay = DateTimeUtils.absoluteDayFromDateValue(a[0]); long absoluteDay = absoluteDayFromDateValue(a[0]);
long timeNanos = a[1]; long timeNanos = a[1];
BigInteger[] dr = a2.divideAndRemainder(NANOS_PER_DAY_BI); BigInteger[] dr = a2.divideAndRemainder(NANOS_PER_DAY_BI);
if (opType == IntervalOpType.DATETIME_PLUS_INTERVAL) { if (opType == IntervalOpType.DATETIME_PLUS_INTERVAL) {
...@@ -231,15 +234,14 @@ public class IntervalOperation extends Expression { ...@@ -231,15 +234,14 @@ public class IntervalOperation extends Expression {
absoluteDay -= dr[0].longValue(); absoluteDay -= dr[0].longValue();
timeNanos -= dr[1].longValue(); timeNanos -= dr[1].longValue();
} }
if (timeNanos >= DateTimeUtils.NANOS_PER_DAY) { if (timeNanos >= NANOS_PER_DAY) {
timeNanos -= DateTimeUtils.NANOS_PER_DAY; timeNanos -= NANOS_PER_DAY;
absoluteDay++; absoluteDay++;
} else if (timeNanos < 0) { } else if (timeNanos < 0) {
timeNanos += DateTimeUtils.NANOS_PER_DAY; timeNanos += NANOS_PER_DAY;
absoluteDay--; absoluteDay--;
} }
return DateTimeUtils.dateTimeToValue(l, DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay), return dateTimeToValue(l, dateValueFromAbsoluteDay(absoluteDay), timeNanos, false);
timeNanos, false);
} }
} }
} }
......
...@@ -775,3 +775,12 @@ DROP TABLE TEST; ...@@ -775,3 +775,12 @@ DROP TABLE TEST;
CREATE TABLE TEST(I INTERVAL HOUR TO SECOND(10)); CREATE TABLE TEST(I INTERVAL HOUR TO SECOND(10));
> exception INVALID_VALUE_SCALE_PRECISION > exception INVALID_VALUE_SCALE_PRECISION
SELECT TIMESTAMP '2018-09-10 23:30:00' - TIMESTAMP '2014-09-11 23:30:00';
>> INTERVAL '1460 00:00:00' DAY TO SECOND
SELECT TIMESTAMP WITH TIME ZONE '2014-09-11 23:30:00Z' - TIMESTAMP WITH TIME ZONE '2018-09-10 23:30:00Z';
>> INTERVAL '-1460 00:00:00' DAY TO SECOND
SELECT DATE '2018-09-10' - DATE '2014-09-11';
>> INTERVAL '1460' DAY
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论