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

Extract IntervalUtils from DateTimeUtils

上级 f6a0794f
......@@ -9,7 +9,7 @@ import static org.h2.util.DateTimeUtils.NANOS_PER_MINUTE;
import static org.h2.util.DateTimeUtils.NANOS_PER_SECOND;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
/**
* INTERVAL representation for result sets.
......@@ -471,7 +471,7 @@ public final class Interval {
public Interval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
this.qualifier = qualifier;
try {
this.negative = DateTimeUtils.validateInterval(qualifier, negative, leading, remaining);
this.negative = IntervalUtils.validateInterval(qualifier, negative, leading, remaining);
} catch (DbException e) {
throw new IllegalArgumentException();
}
......@@ -523,7 +523,7 @@ public final class Interval {
* @return years, or 0
*/
public long getYears() {
return DateTimeUtils.yearsFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.yearsFromInterval(qualifier, negative, leading, remaining);
}
/**
......@@ -532,7 +532,7 @@ public final class Interval {
* @return months, or 0
*/
public long getMonths() {
return DateTimeUtils.monthsFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.monthsFromInterval(qualifier, negative, leading, remaining);
}
/**
......@@ -541,7 +541,7 @@ public final class Interval {
* @return days, or 0
*/
public long getDays() {
return DateTimeUtils.daysFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.daysFromInterval(qualifier, negative, leading, remaining);
}
/**
......@@ -550,7 +550,7 @@ public final class Interval {
* @return hours, or 0
*/
public long getHours() {
return DateTimeUtils.hoursFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.hoursFromInterval(qualifier, negative, leading, remaining);
}
/**
......@@ -559,7 +559,7 @@ public final class Interval {
* @return minutes, or 0
*/
public long getMinutes() {
return DateTimeUtils.minutesFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.minutesFromInterval(qualifier, negative, leading, remaining);
}
/**
......@@ -599,7 +599,7 @@ public final class Interval {
* @return nanoseconds (including seconds), or 0
*/
public long getSecondsAndNanos() {
return DateTimeUtils.nanosFromInterval(qualifier, negative, leading, remaining);
return IntervalUtils.nanosFromInterval(qualifier, negative, leading, remaining);
}
@Override
......@@ -628,7 +628,7 @@ public final class Interval {
@Override
public String toString() {
return DateTimeUtils.intervalToString(qualifier, negative, leading, remaining);
return IntervalUtils.intervalToString(qualifier, negative, leading, remaining);
}
}
......@@ -190,6 +190,7 @@ import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.util.MathUtils;
import org.h2.util.ParserUtil;
import org.h2.util.StatementBuilder;
......@@ -3691,7 +3692,7 @@ public class Parser {
qualifier = IntervalQualifier.SECOND;
}
try {
return ValueExpression.get(DateTimeUtils.parseInterval(qualifier, negative, s));
return ValueExpression.get(IntervalUtils.parseInterval(qualifier, negative, s));
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, e, "INTERVAL", s);
}
......
......@@ -23,6 +23,7 @@ import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueDate;
......@@ -261,9 +262,9 @@ class AggregateDataMedian extends AggregateDataCollecting {
case Value.INTERVAL_HOUR_TO_MINUTE:
case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND:
return DateTimeUtils.intervalFromAbsolute(IntervalQualifier.valueOf(dataType - Value.INTERVAL_YEAR),
DateTimeUtils.intervalToAbsolute((ValueInterval) v0)
.add(DateTimeUtils.intervalToAbsolute((ValueInterval) v1)).shiftRight(1));
return IntervalUtils.intervalFromAbsolute(IntervalQualifier.valueOf(dataType - Value.INTERVAL_YEAR),
IntervalUtils.intervalToAbsolute((ValueInterval) v0)
.add(IntervalUtils.intervalToAbsolute((ValueInterval) v1)).shiftRight(1));
default:
// Just return first
return v0.convertTo(dataType);
......
......@@ -16,6 +16,7 @@ import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueDate;
......@@ -131,9 +132,9 @@ public class IntervalOperation extends Expression {
switch (opType) {
case INTERVAL_PLUS_INTERVAL:
case INTERVAL_MINUS_INTERVAL: {
BigInteger a1 = DateTimeUtils.intervalToAbsolute((ValueInterval) l);
BigInteger a2 = DateTimeUtils.intervalToAbsolute((ValueInterval) r);
return DateTimeUtils.intervalFromAbsolute(
BigInteger a1 = IntervalUtils.intervalToAbsolute((ValueInterval) l);
BigInteger a2 = IntervalUtils.intervalToAbsolute((ValueInterval) r);
return IntervalUtils.intervalFromAbsolute(
IntervalQualifier.valueOf(Value.getHigherOrder(lType, rType) - Value.INTERVAL_YEAR),
opType == IntervalOpType.INTERVAL_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2));
}
......@@ -142,9 +143,9 @@ public class IntervalOperation extends Expression {
return getDateTimeWithInterval(l, r, lType, rType);
case INTERVAL_MULTIPLY_NUMERIC:
case INTERVAL_DIVIDE_NUMERIC: {
BigDecimal a1 = new BigDecimal(DateTimeUtils.intervalToAbsolute((ValueInterval) l));
BigDecimal a1 = new BigDecimal(IntervalUtils.intervalToAbsolute((ValueInterval) l));
BigDecimal a2 = r.getBigDecimal();
return DateTimeUtils.intervalFromAbsolute(IntervalQualifier.valueOf(lType - Value.INTERVAL_YEAR),
return IntervalUtils.intervalFromAbsolute(IntervalQualifier.valueOf(lType - Value.INTERVAL_YEAR),
(opType == IntervalOpType.INTERVAL_MULTIPLY_NUMERIC ? a1.multiply(a2) : a1.divide(a2))
.toBigInteger());
}
......@@ -179,7 +180,7 @@ public class IntervalOperation extends Expression {
diff = diff.add(BigInteger.valueOf((((ValueTimestampTimeZone) r).getTimeZoneOffsetMins()
- ((ValueTimestampTimeZone) l).getTimeZoneOffsetMins()) * 60_000_000_000L));
}
return DateTimeUtils.intervalFromAbsolute(IntervalQualifier.DAY_TO_SECOND, diff);
return IntervalUtils.intervalFromAbsolute(IntervalQualifier.DAY_TO_SECOND, diff);
}
}
throw DbException.throwInternalError("type=" + opType);
......@@ -192,7 +193,7 @@ public class IntervalOperation extends Expression {
throw DbException.throwInternalError("type=" + rType);
}
BigInteger a1 = BigInteger.valueOf(((ValueTime) l).getNanos());
BigInteger a2 = DateTimeUtils.intervalToAbsolute((ValueInterval) r);
BigInteger a2 = IntervalUtils.intervalToAbsolute((ValueInterval) r);
BigInteger n = opType == IntervalOpType.DATETIME_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2);
if (n.signum() < 0 || n.compareTo(BigInteger.valueOf(DateTimeUtils.NANOS_PER_DAY)) >= 0) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, n.toString());
......@@ -203,13 +204,13 @@ public class IntervalOperation extends Expression {
case Value.TIMESTAMP:
case Value.TIMESTAMP_TZ:
if (DataType.isYearMonthIntervalType(rType)) {
long m = DateTimeUtils.intervalToAbsolute((ValueInterval) r).longValue();
long m = IntervalUtils.intervalToAbsolute((ValueInterval) r).longValue();
if (opType == IntervalOpType.DATETIME_MINUS_INTERVAL) {
m = -m;
}
return DateTimeFunctions.dateadd("MONTH", m, l);
} else {
BigInteger a2 = DateTimeUtils.intervalToAbsolute((ValueInterval) r);
BigInteger a2 = IntervalUtils.intervalToAbsolute((ValueInterval) r);
if (lType == Value.DATE) {
BigInteger a1 = BigInteger
.valueOf(DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue()));
......
......@@ -355,7 +355,7 @@ public final class DateTimeFunctions {
bd = bd.negate();
}
} else {
bd = new BigDecimal(DateTimeUtils.intervalToAbsolute(interval))
bd = new BigDecimal(IntervalUtils.intervalToAbsolute(interval))
.divide(BigDecimal.valueOf(NANOS_PER_SECOND));
}
return ValueDecimal.get(bd);
......@@ -634,33 +634,33 @@ public final class DateTimeFunctions {
long v;
switch (field) {
case YEAR:
v = DateTimeUtils.yearsFromInterval(qualifier, negative, leading, remaining);
v = IntervalUtils.yearsFromInterval(qualifier, negative, leading, remaining);
break;
case MONTH:
v = DateTimeUtils.monthsFromInterval(qualifier, negative, leading, remaining);
v = IntervalUtils.monthsFromInterval(qualifier, negative, leading, remaining);
break;
case DAY_OF_MONTH:
case DAY_OF_WEEK:
case DAY_OF_YEAR:
v = DateTimeUtils.daysFromInterval(qualifier, negative, leading, remaining);
v = IntervalUtils.daysFromInterval(qualifier, negative, leading, remaining);
break;
case HOUR:
v = DateTimeUtils.hoursFromInterval(qualifier, negative, leading, remaining);
v = IntervalUtils.hoursFromInterval(qualifier, negative, leading, remaining);
break;
case MINUTE:
v = DateTimeUtils.minutesFromInterval(qualifier, negative, leading, remaining);
v = IntervalUtils.minutesFromInterval(qualifier, negative, leading, remaining);
break;
case SECOND:
v = DateTimeUtils.nanosFromInterval(qualifier, negative, leading, remaining) / NANOS_PER_SECOND;
v = IntervalUtils.nanosFromInterval(qualifier, negative, leading, remaining) / NANOS_PER_SECOND;
break;
case MILLISECOND:
v = DateTimeUtils.nanosFromInterval(qualifier, negative, leading, remaining) / 1_000_000 % 1_000;
v = IntervalUtils.nanosFromInterval(qualifier, negative, leading, remaining) / 1_000_000 % 1_000;
break;
case MICROSECOND:
v = DateTimeUtils.nanosFromInterval(qualifier, negative, leading, remaining) / 1_000 % 1_000_000;
v = IntervalUtils.nanosFromInterval(qualifier, negative, leading, remaining) / 1_000 % 1_000_000;
break;
case NANOSECOND:
v = DateTimeUtils.nanosFromInterval(qualifier, negative, leading, remaining) % NANOS_PER_SECOND;
v = IntervalUtils.nanosFromInterval(qualifier, negative, leading, remaining) % NANOS_PER_SECOND;
break;
default:
throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
......
......@@ -6,20 +6,15 @@
*/
package org.h2.util;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.engine.Mode;
import org.h2.message.DbException;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueInterval;
import org.h2.value.ValueNull;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
......@@ -461,7 +456,7 @@ public class DateTimeUtils {
return ((((hour * 60L) + minute) * 60) + second) * NANOS_PER_SECOND + nanos;
}
private static int parseNanos(String s, int start, int end) {
static int parseNanos(String s, int start, int end) {
if (start >= end) {
throw new IllegalArgumentException(s);
}
......@@ -1480,7 +1475,7 @@ public class DateTimeUtils {
}
}
private static void stripTrailingZeroes(StringBuilder buff) {
static void stripTrailingZeroes(StringBuilder buff) {
int i = buff.length() - 1;
if (buff.charAt(i) == '0') {
while (buff.charAt(--i) == '0') {
......@@ -1555,414 +1550,6 @@ public class DateTimeUtils {
return b.toString();
}
/**
* Parses the specified string as {@code INTERVAL} value.
*
* @param qualifier the default qualifier to use if string does not have one
* @param s the string with type information to parse
* @return the interval value.
* Type of value can be different from the specified qualifier.
*/
public static ValueInterval parseFormattedInterval(IntervalQualifier qualifier, String s) {
int i = 0;
i = skipWS(s, i);
if (!s.regionMatches(true, i, "INTERVAL", 0, 8)) {
return parseInterval(qualifier, false, s);
}
i = skipWS(s, i + 8);
boolean negative = false;
char ch = s.charAt(i);
if (ch == '-') {
negative = true;
i = skipWS(s, i + 1);
ch = s.charAt(i);
} else if (ch == '+') {
i = skipWS(s, i + 1);
ch = s.charAt(i);
}
if (ch != '\'') {
throw new IllegalArgumentException(s);
}
int start = ++i;
int l = s.length();
for (;;) {
if (i == l) {
throw new IllegalArgumentException(s);
}
if (s.charAt(i) == '\'') {
break;
}
i++;
}
String v = s.substring(start, i);
i = skipWS(s, i + 1);
if (s.regionMatches(true, i, "YEAR", 0, 4)) {
i += 4;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.YEAR, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j && s.regionMatches(true, i, "MONTH", 0, 5)) {
if (skipWSEnd(s, i + 5) == l) {
return parseInterval(IntervalQualifier.YEAR_TO_MONTH, negative, v);
}
}
}
} else if (s.regionMatches(true, i, "MONTH", 0, 5)) {
if (skipWSEnd(s, i + 5) == l) {
return parseInterval(IntervalQualifier.MONTH, negative, v);
}
} if (s.regionMatches(true, i, "DAY", 0, 3)) {
i += 3;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.DAY, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j) {
if (s.regionMatches(true, i, "HOUR", 0, 4)) {
if (skipWSEnd(s, i + 4) == l) {
return parseInterval(IntervalQualifier.DAY_TO_HOUR, negative, v);
}
} else if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.DAY_TO_MINUTE, negative, v);
}
} else if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.DAY_TO_SECOND, negative, v);
}
}
}
}
} if (s.regionMatches(true, i, "HOUR", 0, 4)) {
i += 4;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.HOUR, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j) {
if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.HOUR_TO_MINUTE, negative, v);
}
} else if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.HOUR_TO_SECOND, negative, v);
}
}
}
}
} if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
i += 6;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.MINUTE, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j && s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.MINUTE_TO_SECOND, negative, v);
}
}
}
} if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.SECOND, negative, v);
}
}
throw new IllegalArgumentException(s);
}
private static int skipWS(String s, int i) {
for (int l = s.length(); ; i++) {
if (i == l) {
throw new IllegalArgumentException(s);
}
if (!Character.isWhitespace(s.charAt(i))) {
return i;
}
}
}
private static int skipWSEnd(String s, int i) {
for (int l = s.length(); ; i++) {
if (i == l) {
return i;
}
if (!Character.isWhitespace(s.charAt(i))) {
return i;
}
}
}
/**
* Parses the specified string as {@code INTERVAL} value.
*
* @param qualifier the qualifier of interval
* @param negative whether the interval is negative
* @param s the string to parse
* @return the interval value
*/
public static ValueInterval parseInterval(IntervalQualifier qualifier, boolean negative, String s) {
long leading, remaining;
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
break;
case SECOND: {
int dot = s.indexOf('.');
if (dot < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dot, negative);
remaining = parseNanos(s, dot + 1, s.length());
}
break;
}
case YEAR_TO_MONTH:
return parseInterval2(qualifier, s, '-', 11, negative);
case DAY_TO_HOUR:
return parseInterval2(qualifier, s, ' ', 23, negative);
case DAY_TO_MINUTE: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * 60;
} else {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * 60
+ parseIntervalRemaining(s, colon + 1, s.length(), 59);
}
}
break;
}
case DAY_TO_SECOND: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * NANOS_PER_HOUR;
} else {
int colon2 = s.indexOf(':', colon + 1);
if (colon2 < 0) {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * NANOS_PER_HOUR
+ parseIntervalRemaining(s, colon + 1, s.length(), 59) * NANOS_PER_MINUTE;
} else {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * NANOS_PER_HOUR
+ parseIntervalRemaining(s, colon + 1, colon2, 59) * NANOS_PER_MINUTE
+ parseIntervalRemainingSeconds(s, colon2 + 1);
}
}
}
break;
}
case HOUR_TO_MINUTE:
return parseInterval2(qualifier, s, ':', 59, negative);
case HOUR_TO_SECOND: {
int colon = s.indexOf(':');
if (colon < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, colon, negative);
int colon2 = s.indexOf(':', colon + 1);
if (colon2 < 0) {
remaining = parseIntervalRemaining(s, colon + 1, s.length(), 59) * NANOS_PER_MINUTE;
} else {
remaining = parseIntervalRemaining(s, colon + 1, colon2, 59) * NANOS_PER_MINUTE
+ parseIntervalRemainingSeconds(s, colon2 + 1);
}
}
break;
}
case MINUTE_TO_SECOND: {
int dash = s.indexOf(':');
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemainingSeconds(s, dash + 1);
}
break;
}
default:
throw new IllegalArgumentException();
}
negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
}
static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max, boolean negative) {
long leading;
long remaining;
int dash = s.indexOf(ch, 1);
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemaining(s, dash + 1, s.length(), max);
}
negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
}
private static long parseIntervalLeading(String s, int start, int end, boolean negative) {
long leading = Long.parseLong(s.substring(start, end));
if (leading == 0) {
return negative ^ s.charAt(start) == '-' ? Long.MIN_VALUE : 0;
}
return negative ? -leading : leading;
}
private static long parseIntervalRemaining(String s, int start, int end, int max) {
int v = StringUtils.parseUInt31(s, start, end);
if (v > max) {
throw new IllegalArgumentException(s);
}
return v;
}
private static long parseIntervalRemainingSeconds(String s, int start) {
int seconds, nanos;
int dot = s.indexOf('.', start + 1);
if (dot < 0) {
seconds = StringUtils.parseUInt31(s, start, s.length());
nanos = 0;
} else {
seconds = StringUtils.parseUInt31(s, start, dot);
nanos = parseNanos(s, dot + 1, s.length());
}
if (seconds > 59) {
throw new IllegalArgumentException(s);
}
return seconds * NANOS_PER_SECOND + nanos;
}
/**
* Formats interval as a string.
*
* @param qualifier qualifier of the interval
* @param negative whether interval is negative
* @param leading the value of leading field
* @param remaining the value of all remaining fields
* @return string representation of the specified interval
*/
public static String intervalToString(IntervalQualifier qualifier, boolean negative, long leading, long remaining)
{
StringBuilder buff = new StringBuilder().append("INTERVAL ");
buff.append('\'');
if (negative) {
buff.append('-');
}
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
buff.append(leading);
break;
case SECOND:
buff.append(leading);
appendNanos(buff, remaining);
break;
case YEAR_TO_MONTH:
buff.append(leading).append('-').append(remaining);
break;
case DAY_TO_HOUR:
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining);
break;
case DAY_TO_MINUTE:
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining / 60);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, remaining % 60);
break;
case DAY_TO_SECOND: {
long nanos = remaining % NANOS_PER_MINUTE;
remaining /= NANOS_PER_MINUTE;
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining / 60);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, remaining % 60);
buff.append(':');
appendSecondsWithNanos(buff, nanos);
break;
}
case HOUR_TO_MINUTE:
buff.append(leading).append(':');
StringUtils.appendZeroPadded(buff, 2, remaining);
break;
case HOUR_TO_SECOND:
buff.append(leading).append(':');
StringUtils.appendZeroPadded(buff, 2, remaining / NANOS_PER_MINUTE);
buff.append(':');
appendSecondsWithNanos(buff, remaining % NANOS_PER_MINUTE);
break;
case MINUTE_TO_SECOND:
buff.append(leading).append(':');
appendSecondsWithNanos(buff, remaining);
break;
}
buff.append("' ").append(qualifier);
return buff.toString();
}
private static void appendSecondsWithNanos(StringBuilder buff, long nanos) {
StringUtils.appendZeroPadded(buff, 2, nanos / NANOS_PER_SECOND);
appendNanos(buff, nanos % NANOS_PER_SECOND);
}
private static void appendNanos(StringBuilder buff, long nanos) {
if (nanos > 0) {
buff.append('.');
StringUtils.appendZeroPadded(buff, 9, nanos);
stripTrailingZeroes(buff);
}
}
/**
* Converts scale of nanoseconds.
*
......@@ -1982,380 +1569,4 @@ public class DateTimeUtils {
return nanosOfDay - mod;
}
/**
* Converts interval value to an absolute value.
*
* @param interval the interval value
* @return absolute value in months for year-month intervals,
* in nanoseconds for day-time intervals
*/
public static BigInteger intervalToAbsolute(ValueInterval interval) {
BigInteger r;
switch (interval.getQualifier()) {
case YEAR:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(12));
break;
case MONTH:
r = BigInteger.valueOf(interval.getLeading());
break;
case DAY:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_DAY));
break;
case HOUR:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_HOUR));
break;
case MINUTE:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_MINUTE));
break;
case SECOND:
r = intervalToAbsolute(interval, NANOS_PER_SECOND);
break;
case YEAR_TO_MONTH:
r = intervalToAbsolute(interval, 12);
break;
case DAY_TO_HOUR:
r = intervalToAbsolute(interval, 24, NANOS_PER_HOUR);
break;
case DAY_TO_MINUTE:
r = intervalToAbsolute(interval, 24 * 60, NANOS_PER_MINUTE);
break;
case DAY_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_DAY);
break;
case HOUR_TO_MINUTE:
r = intervalToAbsolute(interval, 60, NANOS_PER_MINUTE);
break;
case HOUR_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_HOUR);
break;
case MINUTE_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_MINUTE);
break;
default:
throw new IllegalArgumentException();
}
return interval.isNegative() ? r.negate() : r;
}
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier, long totalMultiplier) {
return intervalToAbsolute(interval, multiplier).multiply(BigInteger.valueOf(totalMultiplier));
}
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier) {
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(multiplier))
.add(BigInteger.valueOf(interval.getRemaining()));
}
/**
* Converts absolute value to an interval value.
*
* @param qualifier the qualifier of interval
* @param absolute absolute value in months for year-month intervals,
* in nanoseconds for day-time intervals
* @return the interval value
*/
public static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute) {
switch (qualifier) {
case YEAR:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(12))), 0);
case MONTH:
return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(absolute), 0);
case DAY:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_DAY))), 0);
case HOUR:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_HOUR))), 0);
case MINUTE:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE))), 0);
case SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_SECOND);
case YEAR_TO_MONTH:
return intervalFromAbsolute(qualifier, absolute, 12);
case DAY_TO_HOUR:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_HOUR)), 24);
case DAY_TO_MINUTE:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE)), 24 * 60);
case DAY_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_DAY);
case HOUR_TO_MINUTE:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE)), 60);
case HOUR_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_HOUR);
case MINUTE_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_MINUTE);
default:
throw new IllegalArgumentException();
}
}
private static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute, long divisor) {
BigInteger[] dr = absolute.divideAndRemainder(BigInteger.valueOf(divisor));
return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(dr[0]), Math.abs(dr[1].longValue()));
}
private static long leadingExact(BigInteger absolute) {
if (absolute.compareTo(BigInteger.valueOf(999_999_999_999_999_999L)) > 0
|| absolute.compareTo(BigInteger.valueOf(-999_999_999_999_999_999L)) < 0) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, absolute.toString());
}
return Math.abs(absolute.longValue());
}
/**
* Ensures that all fields in interval are valid.
*
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return fixed value of negative field
*/
public static boolean validateInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining)
{
if (qualifier == null) {
throw new NullPointerException();
}
if (leading == 0L && remaining == 0L) {
return false;
}
// Upper bound for remaining value (exclusive)
long bound;
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
bound = 1;
break;
case SECOND:
bound = NANOS_PER_SECOND;
break;
case YEAR_TO_MONTH:
bound = 12;
break;
case DAY_TO_HOUR:
bound = 24;
break;
case DAY_TO_MINUTE:
bound = 24 * 60;
break;
case DAY_TO_SECOND:
bound = NANOS_PER_DAY;
break;
case HOUR_TO_MINUTE:
bound = 60;
break;
case HOUR_TO_SECOND:
bound = NANOS_PER_HOUR;
break;
case MINUTE_TO_SECOND:
bound = NANOS_PER_MINUTE;
break;
default:
throw DbException.getInvalidValueException("interval", qualifier);
}
if (leading < 0L || leading >= 1_000_000_000_000_000_000L) {
throw DbException.getInvalidValueException("interval", Long.toString(leading));
}
if (remaining < 0L || remaining >= bound) {
throw DbException.getInvalidValueException("interval", Long.toString(remaining));
}
return negative;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return years, or 0
*/
public static long yearsFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
if (qualifier == IntervalQualifier.YEAR || qualifier == IntervalQualifier.YEAR_TO_MONTH) {
long v = leading;
if (negative) {
v = -v;
}
return v;
} else {
return 0;
}
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return months, or 0
*/
public static long monthsFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining)
{
long v;
if (qualifier == IntervalQualifier.MONTH) {
v = leading;
} else if (qualifier == IntervalQualifier.YEAR_TO_MONTH){
v = remaining;
} else {
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return months, or 0
*/
public static long daysFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
switch (qualifier) {
case DAY:
case DAY_TO_HOUR:
case DAY_TO_MINUTE:
case DAY_TO_SECOND:
long v = leading;
if (negative) {
v = -v;
}
return v;
default:
return 0;
}
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return hours, or 0
*/
public static long hoursFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
long v;
switch (qualifier) {
case HOUR:
case HOUR_TO_MINUTE:
case HOUR_TO_SECOND:
v = leading;
break;
case DAY_TO_HOUR:
v = remaining;
break;
case DAY_TO_MINUTE:
v = remaining / 60;
break;
case DAY_TO_SECOND:
v = remaining / NANOS_PER_HOUR;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return minutes, or 0
*/
public static long minutesFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining)
{
long v;
switch (qualifier) {
case MINUTE:
case MINUTE_TO_SECOND:
v = leading;
break;
case DAY_TO_MINUTE:
v = remaining % 60;
break;
case DAY_TO_SECOND:
v = remaining / NANOS_PER_MINUTE % 60;
break;
case HOUR_TO_MINUTE:
v = remaining;
break;
case HOUR_TO_SECOND:
v = remaining / NANOS_PER_MINUTE;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return nanoseconds, or 0
*/
public static long nanosFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
long v;
switch (qualifier) {
case SECOND:
v = leading * NANOS_PER_SECOND + remaining;
break;
case DAY_TO_SECOND:
case HOUR_TO_SECOND:
v = remaining % NANOS_PER_MINUTE;
break;
case MINUTE_TO_SECOND:
v = remaining;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import static org.h2.util.DateTimeUtils.NANOS_PER_DAY;
import static org.h2.util.DateTimeUtils.NANOS_PER_HOUR;
import static org.h2.util.DateTimeUtils.NANOS_PER_MINUTE;
import static org.h2.util.DateTimeUtils.NANOS_PER_SECOND;
import java.math.BigInteger;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException;
import org.h2.value.ValueInterval;
/**
* This utility class contains interval conversion functions.
*/
public class IntervalUtils {
private IntervalUtils() {
// utility class
}
/**
* Parses the specified string as {@code INTERVAL} value.
*
* @param qualifier
* the default qualifier to use if string does not have one
* @param s
* the string with type information to parse
* @return the interval value. Type of value can be different from the
* specified qualifier.
*/
public static ValueInterval parseFormattedInterval(IntervalQualifier qualifier, String s) {
int i = 0;
i = skipWS(s, i);
if (!s.regionMatches(true, i, "INTERVAL", 0, 8)) {
return parseInterval(qualifier, false, s);
}
i = skipWS(s, i + 8);
boolean negative = false;
char ch = s.charAt(i);
if (ch == '-') {
negative = true;
i = skipWS(s, i + 1);
ch = s.charAt(i);
} else if (ch == '+') {
i = skipWS(s, i + 1);
ch = s.charAt(i);
}
if (ch != '\'') {
throw new IllegalArgumentException(s);
}
int start = ++i;
int l = s.length();
for (;;) {
if (i == l) {
throw new IllegalArgumentException(s);
}
if (s.charAt(i) == '\'') {
break;
}
i++;
}
String v = s.substring(start, i);
i = skipWS(s, i + 1);
if (s.regionMatches(true, i, "YEAR", 0, 4)) {
i += 4;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.YEAR, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j && s.regionMatches(true, i, "MONTH", 0, 5)) {
if (skipWSEnd(s, i + 5) == l) {
return parseInterval(IntervalQualifier.YEAR_TO_MONTH, negative, v);
}
}
}
} else if (s.regionMatches(true, i, "MONTH", 0, 5)) {
if (skipWSEnd(s, i + 5) == l) {
return parseInterval(IntervalQualifier.MONTH, negative, v);
}
}
if (s.regionMatches(true, i, "DAY", 0, 3)) {
i += 3;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.DAY, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j) {
if (s.regionMatches(true, i, "HOUR", 0, 4)) {
if (skipWSEnd(s, i + 4) == l) {
return parseInterval(IntervalQualifier.DAY_TO_HOUR, negative, v);
}
} else if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.DAY_TO_MINUTE, negative, v);
}
} else if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.DAY_TO_SECOND, negative, v);
}
}
}
}
}
if (s.regionMatches(true, i, "HOUR", 0, 4)) {
i += 4;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.HOUR, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j) {
if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.HOUR_TO_MINUTE, negative, v);
}
} else if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.HOUR_TO_SECOND, negative, v);
}
}
}
}
}
if (s.regionMatches(true, i, "MINUTE", 0, 6)) {
i += 6;
int j = skipWSEnd(s, i);
if (j == l) {
return parseInterval(IntervalQualifier.MINUTE, negative, v);
}
if (j > i && s.regionMatches(true, j, "TO", 0, 2)) {
j += 2;
i = skipWS(s, j);
if (i > j && s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.MINUTE_TO_SECOND, negative, v);
}
}
}
}
if (s.regionMatches(true, i, "SECOND", 0, 6)) {
if (skipWSEnd(s, i + 6) == l) {
return parseInterval(IntervalQualifier.SECOND, negative, v);
}
}
throw new IllegalArgumentException(s);
}
private static int skipWS(String s, int i) {
for (int l = s.length();; i++) {
if (i == l) {
throw new IllegalArgumentException(s);
}
if (!Character.isWhitespace(s.charAt(i))) {
return i;
}
}
}
private static int skipWSEnd(String s, int i) {
for (int l = s.length();; i++) {
if (i == l) {
return i;
}
if (!Character.isWhitespace(s.charAt(i))) {
return i;
}
}
}
/**
* Parses the specified string as {@code INTERVAL} value.
*
* @param qualifier
* the qualifier of interval
* @param negative
* whether the interval is negative
* @param s
* the string to parse
* @return the interval value
*/
public static ValueInterval parseInterval(IntervalQualifier qualifier, boolean negative, String s) {
long leading, remaining;
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
break;
case SECOND: {
int dot = s.indexOf('.');
if (dot < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dot, negative);
remaining = DateTimeUtils.parseNanos(s, dot + 1, s.length());
}
break;
}
case YEAR_TO_MONTH:
return parseInterval2(qualifier, s, '-', 11, negative);
case DAY_TO_HOUR:
return parseInterval2(qualifier, s, ' ', 23, negative);
case DAY_TO_MINUTE: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * 60;
} else {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * 60
+ parseIntervalRemaining(s, colon + 1, s.length(), 59);
}
}
break;
}
case DAY_TO_SECOND: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * NANOS_PER_HOUR;
} else {
int colon2 = s.indexOf(':', colon + 1);
if (colon2 < 0) {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * NANOS_PER_HOUR
+ parseIntervalRemaining(s, colon + 1, s.length(), 59) * NANOS_PER_MINUTE;
} else {
remaining = parseIntervalRemaining(s, space + 1, colon, 23) * NANOS_PER_HOUR
+ parseIntervalRemaining(s, colon + 1, colon2, 59) * NANOS_PER_MINUTE
+ parseIntervalRemainingSeconds(s, colon2 + 1);
}
}
}
break;
}
case HOUR_TO_MINUTE:
return parseInterval2(qualifier, s, ':', 59, negative);
case HOUR_TO_SECOND: {
int colon = s.indexOf(':');
if (colon < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, colon, negative);
int colon2 = s.indexOf(':', colon + 1);
if (colon2 < 0) {
remaining = parseIntervalRemaining(s, colon + 1, s.length(), 59) * NANOS_PER_MINUTE;
} else {
remaining = parseIntervalRemaining(s, colon + 1, colon2, 59) * NANOS_PER_MINUTE
+ parseIntervalRemainingSeconds(s, colon2 + 1);
}
}
break;
}
case MINUTE_TO_SECOND: {
int dash = s.indexOf(':');
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemainingSeconds(s, dash + 1);
}
break;
}
default:
throw new IllegalArgumentException();
}
negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
}
static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max, boolean negative) {
long leading;
long remaining;
int dash = s.indexOf(ch, 1);
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemaining(s, dash + 1, s.length(), max);
}
negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
}
private static long parseIntervalLeading(String s, int start, int end, boolean negative) {
long leading = Long.parseLong(s.substring(start, end));
if (leading == 0) {
return negative ^ s.charAt(start) == '-' ? Long.MIN_VALUE : 0;
}
return negative ? -leading : leading;
}
private static long parseIntervalRemaining(String s, int start, int end, int max) {
int v = StringUtils.parseUInt31(s, start, end);
if (v > max) {
throw new IllegalArgumentException(s);
}
return v;
}
private static long parseIntervalRemainingSeconds(String s, int start) {
int seconds, nanos;
int dot = s.indexOf('.', start + 1);
if (dot < 0) {
seconds = StringUtils.parseUInt31(s, start, s.length());
nanos = 0;
} else {
seconds = StringUtils.parseUInt31(s, start, dot);
nanos = DateTimeUtils.parseNanos(s, dot + 1, s.length());
}
if (seconds > 59) {
throw new IllegalArgumentException(s);
}
return seconds * NANOS_PER_SECOND + nanos;
}
/**
* Formats interval as a string.
*
* @param qualifier
* qualifier of the interval
* @param negative
* whether interval is negative
* @param leading
* the value of leading field
* @param remaining
* the value of all remaining fields
* @return string representation of the specified interval
*/
public static String intervalToString(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
StringBuilder buff = new StringBuilder().append("INTERVAL ");
buff.append('\'');
if (negative) {
buff.append('-');
}
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
buff.append(leading);
break;
case SECOND:
buff.append(leading);
appendNanos(buff, remaining);
break;
case YEAR_TO_MONTH:
buff.append(leading).append('-').append(remaining);
break;
case DAY_TO_HOUR:
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining);
break;
case DAY_TO_MINUTE:
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining / 60);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, remaining % 60);
break;
case DAY_TO_SECOND: {
long nanos = remaining % NANOS_PER_MINUTE;
remaining /= NANOS_PER_MINUTE;
buff.append(leading).append(' ');
StringUtils.appendZeroPadded(buff, 2, remaining / 60);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, remaining % 60);
buff.append(':');
appendSecondsWithNanos(buff, nanos);
break;
}
case HOUR_TO_MINUTE:
buff.append(leading).append(':');
StringUtils.appendZeroPadded(buff, 2, remaining);
break;
case HOUR_TO_SECOND:
buff.append(leading).append(':');
StringUtils.appendZeroPadded(buff, 2, remaining / NANOS_PER_MINUTE);
buff.append(':');
appendSecondsWithNanos(buff, remaining % NANOS_PER_MINUTE);
break;
case MINUTE_TO_SECOND:
buff.append(leading).append(':');
appendSecondsWithNanos(buff, remaining);
break;
}
buff.append("' ").append(qualifier);
return buff.toString();
}
private static void appendSecondsWithNanos(StringBuilder buff, long nanos) {
StringUtils.appendZeroPadded(buff, 2, nanos / NANOS_PER_SECOND);
appendNanos(buff, nanos % NANOS_PER_SECOND);
}
private static void appendNanos(StringBuilder buff, long nanos) {
if (nanos > 0) {
buff.append('.');
StringUtils.appendZeroPadded(buff, 9, nanos);
DateTimeUtils.stripTrailingZeroes(buff);
}
}
/**
* Converts interval value to an absolute value.
*
* @param interval
* the interval value
* @return absolute value in months for year-month intervals, in nanoseconds
* for day-time intervals
*/
public static BigInteger intervalToAbsolute(ValueInterval interval) {
BigInteger r;
switch (interval.getQualifier()) {
case YEAR:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(12));
break;
case MONTH:
r = BigInteger.valueOf(interval.getLeading());
break;
case DAY:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_DAY));
break;
case HOUR:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_HOUR));
break;
case MINUTE:
r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_MINUTE));
break;
case SECOND:
r = intervalToAbsolute(interval, NANOS_PER_SECOND);
break;
case YEAR_TO_MONTH:
r = intervalToAbsolute(interval, 12);
break;
case DAY_TO_HOUR:
r = intervalToAbsolute(interval, 24, NANOS_PER_HOUR);
break;
case DAY_TO_MINUTE:
r = intervalToAbsolute(interval, 24 * 60, NANOS_PER_MINUTE);
break;
case DAY_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_DAY);
break;
case HOUR_TO_MINUTE:
r = intervalToAbsolute(interval, 60, NANOS_PER_MINUTE);
break;
case HOUR_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_HOUR);
break;
case MINUTE_TO_SECOND:
r = intervalToAbsolute(interval, NANOS_PER_MINUTE);
break;
default:
throw new IllegalArgumentException();
}
return interval.isNegative() ? r.negate() : r;
}
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier, long totalMultiplier) {
return intervalToAbsolute(interval, multiplier).multiply(BigInteger.valueOf(totalMultiplier));
}
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier) {
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(multiplier))
.add(BigInteger.valueOf(interval.getRemaining()));
}
/**
* Converts absolute value to an interval value.
*
* @param qualifier
* the qualifier of interval
* @param absolute
* absolute value in months for year-month intervals, in
* nanoseconds for day-time intervals
* @return the interval value
*/
public static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute) {
switch (qualifier) {
case YEAR:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(12))), 0);
case MONTH:
return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(absolute), 0);
case DAY:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_DAY))), 0);
case HOUR:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_HOUR))), 0);
case MINUTE:
return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE))), 0);
case SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_SECOND);
case YEAR_TO_MONTH:
return intervalFromAbsolute(qualifier, absolute, 12);
case DAY_TO_HOUR:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_HOUR)), 24);
case DAY_TO_MINUTE:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE)), 24 * 60);
case DAY_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_DAY);
case HOUR_TO_MINUTE:
return intervalFromAbsolute(qualifier, absolute.divide(BigInteger.valueOf(NANOS_PER_MINUTE)), 60);
case HOUR_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_HOUR);
case MINUTE_TO_SECOND:
return intervalFromAbsolute(qualifier, absolute, NANOS_PER_MINUTE);
default:
throw new IllegalArgumentException();
}
}
private static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute, long divisor) {
BigInteger[] dr = absolute.divideAndRemainder(BigInteger.valueOf(divisor));
return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(dr[0]), Math.abs(dr[1].longValue()));
}
private static long leadingExact(BigInteger absolute) {
if (absolute.compareTo(BigInteger.valueOf(999_999_999_999_999_999L)) > 0
|| absolute.compareTo(BigInteger.valueOf(-999_999_999_999_999_999L)) < 0) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, absolute.toString());
}
return Math.abs(absolute.longValue());
}
/**
* Ensures that all fields in interval are valid.
*
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return fixed value of negative field
*/
public static boolean validateInterval(IntervalQualifier qualifier, boolean negative, long leading,
long remaining) {
if (qualifier == null) {
throw new NullPointerException();
}
if (leading == 0L && remaining == 0L) {
return false;
}
// Upper bound for remaining value (exclusive)
long bound;
switch (qualifier) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
bound = 1;
break;
case SECOND:
bound = NANOS_PER_SECOND;
break;
case YEAR_TO_MONTH:
bound = 12;
break;
case DAY_TO_HOUR:
bound = 24;
break;
case DAY_TO_MINUTE:
bound = 24 * 60;
break;
case DAY_TO_SECOND:
bound = NANOS_PER_DAY;
break;
case HOUR_TO_MINUTE:
bound = 60;
break;
case HOUR_TO_SECOND:
bound = NANOS_PER_HOUR;
break;
case MINUTE_TO_SECOND:
bound = NANOS_PER_MINUTE;
break;
default:
throw DbException.getInvalidValueException("interval", qualifier);
}
if (leading < 0L || leading >= 1_000_000_000_000_000_000L) {
throw DbException.getInvalidValueException("interval", Long.toString(leading));
}
if (remaining < 0L || remaining >= bound) {
throw DbException.getInvalidValueException("interval", Long.toString(remaining));
}
return negative;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return years, or 0
*/
public static long yearsFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
if (qualifier == IntervalQualifier.YEAR || qualifier == IntervalQualifier.YEAR_TO_MONTH) {
long v = leading;
if (negative) {
v = -v;
}
return v;
} else {
return 0;
}
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return months, or 0
*/
public static long monthsFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
long v;
if (qualifier == IntervalQualifier.MONTH) {
v = leading;
} else if (qualifier == IntervalQualifier.YEAR_TO_MONTH) {
v = remaining;
} else {
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return months, or 0
*/
public static long daysFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
switch (qualifier) {
case DAY:
case DAY_TO_HOUR:
case DAY_TO_MINUTE:
case DAY_TO_SECOND:
long v = leading;
if (negative) {
v = -v;
}
return v;
default:
return 0;
}
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return hours, or 0
*/
public static long hoursFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
long v;
switch (qualifier) {
case HOUR:
case HOUR_TO_MINUTE:
case HOUR_TO_SECOND:
v = leading;
break;
case DAY_TO_HOUR:
v = remaining;
break;
case DAY_TO_MINUTE:
v = remaining / 60;
break;
case DAY_TO_SECOND:
v = remaining / NANOS_PER_HOUR;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return minutes, or 0
*/
public static long minutesFromInterval(IntervalQualifier qualifier, boolean negative, long leading,
long remaining) {
long v;
switch (qualifier) {
case MINUTE:
case MINUTE_TO_SECOND:
v = leading;
break;
case DAY_TO_MINUTE:
v = remaining % 60;
break;
case DAY_TO_SECOND:
v = remaining / NANOS_PER_MINUTE % 60;
break;
case HOUR_TO_MINUTE:
v = remaining;
break;
case HOUR_TO_SECOND:
v = remaining / NANOS_PER_MINUTE;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
/**
* @param qualifier
* qualifier
* @param negative
* whether interval is negative
* @param leading
* value of leading field
* @param remaining
* values of all remaining fields
* @return nanoseconds, or 0
*/
public static long nanosFromInterval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
long v;
switch (qualifier) {
case SECOND:
v = leading * NANOS_PER_SECOND + remaining;
break;
case DAY_TO_SECOND:
case HOUR_TO_SECOND:
v = remaining % NANOS_PER_MINUTE;
break;
case MINUTE_TO_SECOND:
v = remaining;
break;
default:
return 0;
}
if (negative) {
v = -v;
}
return v;
}
}
......@@ -397,7 +397,7 @@ public class LocalDateTimeUtils {
if (DataType.isYearMonthIntervalType(value.getType())) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, (Throwable) null, value.getString());
}
BigInteger[] dr = DateTimeUtils.intervalToAbsolute((ValueInterval) value)
BigInteger[] dr = IntervalUtils.intervalToAbsolute((ValueInterval) value)
.divideAndRemainder(BigInteger.valueOf(1_000_000_000));
try {
return DURATION_OF_SECONDS.invoke(null, dr[0].longValue(), dr[1].longValue());
......
......@@ -28,6 +28,7 @@ import org.h2.store.DataHandler;
import org.h2.tools.SimpleResultSet;
import org.h2.util.Bits;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
......@@ -1234,7 +1235,7 @@ public abstract class Value {
case Value.STRING_FIXED: {
String s = getString();
try {
return (ValueInterval) DateTimeUtils
return (ValueInterval) IntervalUtils
.parseFormattedInterval(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR), s)
.convertTo(targetType);
} catch (Exception e) {
......@@ -1244,8 +1245,8 @@ public abstract class Value {
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_YEAR_TO_MONTH:
return DateTimeUtils.intervalFromAbsolute(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR),
DateTimeUtils.intervalToAbsolute((ValueInterval) this));
return IntervalUtils.intervalFromAbsolute(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR),
IntervalUtils.intervalToAbsolute((ValueInterval) this));
}
throw getDataConversionError(targetType);
}
......@@ -1257,7 +1258,7 @@ public abstract class Value {
case Value.STRING_FIXED: {
String s = getString();
try {
return (ValueInterval) DateTimeUtils
return (ValueInterval) IntervalUtils
.parseFormattedInterval(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR), s)
.convertTo(targetType);
} catch (Exception e) {
......@@ -1274,8 +1275,8 @@ public abstract class Value {
case Value.INTERVAL_HOUR_TO_MINUTE:
case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND:
return DateTimeUtils.intervalFromAbsolute(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR),
DateTimeUtils.intervalToAbsolute((ValueInterval) this));
return IntervalUtils.intervalFromAbsolute(IntervalQualifier.valueOf(targetType - Value.INTERVAL_YEAR),
IntervalUtils.intervalToAbsolute((ValueInterval) this));
}
throw getDataConversionError(targetType);
}
......
......@@ -12,6 +12,7 @@ import org.h2.api.Interval;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
/**
* Implementation of the INTERVAL data type.
......@@ -58,7 +59,7 @@ public class ValueInterval extends Value {
* @return interval value
*/
public static ValueInterval from(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
negative = DateTimeUtils.validateInterval(qualifier, negative, leading, remaining);
negative = IntervalUtils.validateInterval(qualifier, negative, leading, remaining);
return (ValueInterval) Value
.cache(new ValueInterval(qualifier.ordinal() + INTERVAL_YEAR, negative, leading, remaining));
}
......@@ -203,7 +204,7 @@ public class ValueInterval extends Value {
@Override
public String getString() {
return DateTimeUtils.intervalToString(getQualifier(), negative, leading, remaining);
return IntervalUtils.intervalToString(getQualifier(), negative, leading, remaining);
}
@Override
......@@ -298,14 +299,14 @@ public class ValueInterval extends Value {
@Override
public Value add(Value v) {
return DateTimeUtils.intervalFromAbsolute(getQualifier(),
DateTimeUtils.intervalToAbsolute(this).add(DateTimeUtils.intervalToAbsolute((ValueInterval) v)));
return IntervalUtils.intervalFromAbsolute(getQualifier(),
IntervalUtils.intervalToAbsolute(this).add(IntervalUtils.intervalToAbsolute((ValueInterval) v)));
}
@Override
public Value subtract(Value v) {
return DateTimeUtils.intervalFromAbsolute(getQualifier(),
DateTimeUtils.intervalToAbsolute(this).subtract(DateTimeUtils.intervalToAbsolute((ValueInterval) v)));
return IntervalUtils.intervalFromAbsolute(getQualifier(),
IntervalUtils.intervalToAbsolute(this).subtract(IntervalUtils.intervalToAbsolute((ValueInterval) v)));
}
@Override
......
......@@ -15,11 +15,12 @@ import java.util.TimeZone;
import org.h2.api.IntervalQualifier;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.value.ValueInterval;
import org.h2.value.ValueTimestamp;
/**
* Unit tests for the DateTimeUtils class
* Unit tests for the DateTimeUtils and IntervalUtils classes.
*/
public class TestDateTimeUtils extends TestBase {
......@@ -257,7 +258,7 @@ public class TestDateTimeUtils extends TestBase {
private void testParseIntervalImpl(IntervalQualifier qualifier, boolean negative, long leading, long remaining,
String s, String full) {
ValueInterval expected = ValueInterval.from(qualifier, negative, leading, remaining);
assertEquals(expected, DateTimeUtils.parseInterval(qualifier, negative, s));
assertEquals(expected, IntervalUtils.parseInterval(qualifier, negative, s));
StringBuilder b = new StringBuilder();
b.append("INTERVAL ").append('\'');
if (negative) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论