提交 18f836ec authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add support of INTERVAL data type to EXTRACT function

上级 ff41fdeb
...@@ -37,10 +37,12 @@ import java.util.TimeZone; ...@@ -37,10 +37,12 @@ import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.expression.Function; import org.h2.expression.Function;
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.ValueDecimal; import org.h2.value.ValueDecimal;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
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;
...@@ -331,8 +333,25 @@ public final class DateTimeFunctions { ...@@ -331,8 +333,25 @@ public final class DateTimeFunctions {
if (field != EPOCH) { if (field != EPOCH) {
result = ValueInt.get(getIntDatePart(value, field)); result = ValueInt.get(getIntDatePart(value, field));
} else { } else {
// Case where we retrieve the EPOCH time. // Case where we retrieve the EPOCH time.
if (value instanceof ValueInterval) {
ValueInterval interval = (ValueInterval) value;
BigDecimal bd;
if (interval.getQualifier().isYearMonth()) {
interval = (ValueInterval) interval.convertTo(Value.INTERVAL_YEAR_TO_MONTH);
long leading = interval.getLeading();
long remaining = interval.getRemaining();
bd = BigDecimal.valueOf(leading).multiply(BigDecimal.valueOf(31557600))
.add(BigDecimal.valueOf(remaining * 2592000));
if (interval.isNegative()) {
bd = bd.negate();
}
} else {
bd = new BigDecimal(DateTimeUtils.intervalToAbsolute(interval))
.divide(BigDecimal.valueOf(1_000_000_000L));
}
return ValueDecimal.get(bd);
}
// First we retrieve the dateValue and his time in nanoseconds. // First we retrieve the dateValue and his time in nanoseconds.
long[] a = DateTimeUtils.dateAndTimeFromValue(value); long[] a = DateTimeUtils.dateAndTimeFromValue(value);
long dateValue = a[0]; long dateValue = a[0];
...@@ -602,56 +621,95 @@ public final class DateTimeFunctions { ...@@ -602,56 +621,95 @@ public final class DateTimeFunctions {
* @return the value * @return the value
*/ */
public static int getIntDatePart(Value date, int field) { public static int getIntDatePart(Value date, int field) {
long[] a = DateTimeUtils.dateAndTimeFromValue(date); if (date instanceof ValueInterval) {
long dateValue = a[0]; ValueInterval interval = (ValueInterval) date;
long timeNanos = a[1]; long v;
switch (field) { switch (field) {
case YEAR: case YEAR:
return DateTimeUtils.yearFromDateValue(dateValue); v = DateTimeUtils.yearsFromInterval(interval);
case MONTH: break;
return DateTimeUtils.monthFromDateValue(dateValue); case MONTH:
case DAY_OF_MONTH: v = DateTimeUtils.monthFromInterval(interval);
return DateTimeUtils.dayFromDateValue(dateValue); break;
case HOUR: case DAY_OF_MONTH:
return (int) (timeNanos / 3_600_000_000_000L % 24); case DAY_OF_WEEK:
case MINUTE: case DAY_OF_YEAR:
return (int) (timeNanos / 60_000_000_000L % 60); v = DateTimeUtils.daysFromInterval(interval);
case SECOND: break;
return (int) (timeNanos / 1_000_000_000 % 60); case HOUR:
case MILLISECOND: v = DateTimeUtils.hoursFromInterval(interval);
return (int) (timeNanos / 1_000_000 % 1_000); break;
case MICROSECOND: case MINUTE:
return (int) (timeNanos / 1_000 % 1_000_000); v = DateTimeUtils.minutesFromInterval(interval);
case NANOSECOND: break;
return (int) (timeNanos % 1_000_000_000); case SECOND:
case DAY_OF_YEAR: v = DateTimeUtils.nanosFromInterval(interval) / 1_000_000_000;
return DateTimeUtils.getDayOfYear(dateValue); break;
case DAY_OF_WEEK: case MILLISECOND:
return DateTimeUtils.getSundayDayOfWeek(dateValue); v = DateTimeUtils.nanosFromInterval(interval) / 1_000_000 % 1_000;
case WEEK: break;
GregorianCalendar gc = DateTimeUtils.getCalendar(); case MICROSECOND:
return DateTimeUtils.getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek()); v = DateTimeUtils.nanosFromInterval(interval) / 1_000 % 1_000_000;
case QUARTER: break;
return (DateTimeUtils.monthFromDateValue(dateValue) - 1) / 3 + 1; case NANOSECOND:
case ISO_YEAR: v = DateTimeUtils.nanosFromInterval(interval) % 1_000_000_000;
return DateTimeUtils.getIsoWeekYear(dateValue); break;
case ISO_WEEK: default:
return DateTimeUtils.getIsoWeekOfYear(dateValue); throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
case ISO_DAY_OF_WEEK: }
return DateTimeUtils.getIsoDayOfWeek(dateValue); return (int) v;
case TIMEZONE_HOUR: } else {
case TIMEZONE_MINUTE: { long[] a = DateTimeUtils.dateAndTimeFromValue(date);
int offsetMinutes; long dateValue = a[0];
if (date instanceof ValueTimestampTimeZone) { long timeNanos = a[1];
offsetMinutes = ((ValueTimestampTimeZone) date).getTimeZoneOffsetMins(); switch (field) {
} else { case YEAR:
offsetMinutes = DateTimeUtils.getTimeZoneOffsetMillis(null, dateValue, timeNanos); return DateTimeUtils.yearFromDateValue(dateValue);
case MONTH:
return DateTimeUtils.monthFromDateValue(dateValue);
case DAY_OF_MONTH:
return DateTimeUtils.dayFromDateValue(dateValue);
case HOUR:
return (int) (timeNanos / 3_600_000_000_000L % 24);
case MINUTE:
return (int) (timeNanos / 60_000_000_000L % 60);
case SECOND:
return (int) (timeNanos / 1_000_000_000 % 60);
case MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000);
case MICROSECOND:
return (int) (timeNanos / 1_000 % 1_000_000);
case NANOSECOND:
return (int) (timeNanos % 1_000_000_000);
case DAY_OF_YEAR:
return DateTimeUtils.getDayOfYear(dateValue);
case DAY_OF_WEEK:
return DateTimeUtils.getSundayDayOfWeek(dateValue);
case WEEK:
GregorianCalendar gc = DateTimeUtils.getCalendar();
return DateTimeUtils.getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek());
case QUARTER:
return (DateTimeUtils.monthFromDateValue(dateValue) - 1) / 3 + 1;
case ISO_YEAR:
return DateTimeUtils.getIsoWeekYear(dateValue);
case ISO_WEEK:
return DateTimeUtils.getIsoWeekOfYear(dateValue);
case ISO_DAY_OF_WEEK:
return DateTimeUtils.getIsoDayOfWeek(dateValue);
case TIMEZONE_HOUR:
case TIMEZONE_MINUTE: {
int offsetMinutes;
if (date instanceof ValueTimestampTimeZone) {
offsetMinutes = ((ValueTimestampTimeZone) date).getTimeZoneOffsetMins();
} else {
offsetMinutes = DateTimeUtils.getTimeZoneOffsetMillis(null, dateValue, timeNanos);
}
if (field == TIMEZONE_HOUR) {
return offsetMinutes / 60;
}
return offsetMinutes % 60;
} }
if (field == TIMEZONE_HOUR) {
return offsetMinutes / 60;
} }
return offsetMinutes % 60;
}
} }
throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')'); throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
} }
......
...@@ -2039,4 +2039,149 @@ public class DateTimeUtils { ...@@ -2039,4 +2039,149 @@ public class DateTimeUtils {
return Math.abs(absolute.longValue()); return Math.abs(absolute.longValue());
} }
/**
* @param value interval
* @return years, or 0
*/
public static long yearsFromInterval(ValueInterval value) {
IntervalQualifier qualifier = value.getQualifier();
if (qualifier == IntervalQualifier.YEAR || qualifier == IntervalQualifier.YEAR_TO_MONTH) {
long v = value.getLeading();
if (value.isNegative()) {
v = -v;
}
return v;
} else {
return 0;
}
}
/**
* @param value interval
* @return months, or 0
*/
public static long monthFromInterval(ValueInterval value) {
IntervalQualifier qualifier = value.getQualifier();
long v;
if (qualifier == IntervalQualifier.MONTH) {
v = value.getLeading();
} else if (qualifier == IntervalQualifier.YEAR_TO_MONTH){
v = value.getRemaining();
} else {
return 0;
}
if (value.isNegative()) {
v = -v;
}
return v;
}
/**
* @param value interval
* @return months, or 0
*/
public static long daysFromInterval(ValueInterval value) {
switch (value.getQualifier()) {
case DAY:
case DAY_TO_HOUR:
case DAY_TO_MINUTE:
case DAY_TO_SECOND:
long v = value.getLeading();
if (value.isNegative()) {
v = -v;
}
return v;
default:
return 0;
}
}
/**
* @param value interval
* @return hours, or 0
*/
public static long hoursFromInterval(ValueInterval value) {
long v;
switch (value.getQualifier()) {
case HOUR:
case HOUR_TO_MINUTE:
case HOUR_TO_SECOND:
v = value.getLeading();
break;
case DAY_TO_HOUR:
v = value.getRemaining();
break;
case DAY_TO_MINUTE:
v = value.getRemaining() / 60;
break;
case DAY_TO_SECOND:
v = value.getRemaining() / 3_600_000_000_000L;
break;
default:
return 0;
}
if (value.isNegative()) {
v = -v;
}
return v;
}
/**
* @param value interval
* @return minutes, or 0
*/
public static long minutesFromInterval(ValueInterval value) {
long v;
switch (value.getQualifier()) {
case MINUTE:
case MINUTE_TO_SECOND:
v = value.getLeading();
break;
case DAY_TO_MINUTE:
v = value.getRemaining() % 60;
break;
case DAY_TO_SECOND:
v = value.getRemaining() / 60_000_000_000L % 60;
break;
case HOUR_TO_MINUTE:
v = value.getRemaining();
break;
case HOUR_TO_SECOND:
v = value.getRemaining() / 60_000_000_000L;
break;
default:
return 0;
}
if (value.isNegative()) {
v = -v;
}
return v;
}
/**
* @param value interval
* @return nanoseconds, or 0
*/
public static long nanosFromInterval(ValueInterval value) {
long v;
switch (value.getQualifier()) {
case SECOND:
v = value.getLeading() * 1_000_000_000 + value.getRemaining();
break;
case DAY_TO_SECOND:
case HOUR_TO_SECOND:
v = value.getRemaining() % 60_000_000_000L;
break;
case MINUTE_TO_SECOND:
v = value.getRemaining();
break;
default:
return 0;
}
if (value.isNegative()) {
v = -v;
}
return v;
}
} }
...@@ -56,6 +56,24 @@ select EXTRACT (EPOCH from timestamp with time zone '2000-01-03 12:00:00.123456+ ...@@ -56,6 +56,24 @@ select EXTRACT (EPOCH from timestamp with time zone '2000-01-03 12:00:00.123456+
select extract(EPOCH from '2001-02-03 14:15:16'); select extract(EPOCH from '2001-02-03 14:15:16');
>> 981209716 >> 981209716
SELECT EXTRACT(EPOCH FROM INTERVAL '10.1' SECOND);
>> 10.1
SELECT EXTRACT(EPOCH FROM INTERVAL -'0.000001' SECOND);
>> -0.000001
SELECT EXTRACT(EPOCH FROM INTERVAL '0-1' YEAR TO MONTH);
>> 2592000
SELECT EXTRACT(EPOCH FROM INTERVAL '-0-1' YEAR TO MONTH);
>> -2592000
SELECT EXTRACT(EPOCH FROM INTERVAL '1-0' YEAR TO MONTH);
>> 31557600
SELECT EXTRACT(EPOCH FROM INTERVAL '-1-0' YEAR TO MONTH);
>> -31557600
SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00+07:15'); SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00+07:15');
>> 7 >> 7
...@@ -76,3 +94,87 @@ select extract(hour from '2001-02-03 14:15:16'); ...@@ -76,3 +94,87 @@ select extract(hour from '2001-02-03 14:15:16');
select extract(week from timestamp '2001-02-03 14:15:16'); select extract(week from timestamp '2001-02-03 14:15:16');
>> 5 >> 5
SELECT EXTRACT(YEAR FROM INTERVAL '-1' YEAR);
>> -1
SELECT EXTRACT(YEAR FROM INTERVAL '1-2' YEAR TO MONTH);
>> 1
SELECT EXTRACT(MONTH FROM INTERVAL '-1-3' YEAR TO MONTH);
>> -3
SELECT EXTRACT(MONTH FROM INTERVAL '3' MONTH);
>> 3
SELECT EXTRACT(DAY FROM INTERVAL '1100' DAY);
>> 1100
SELECT EXTRACT(DAY FROM INTERVAL '10 23' DAY TO HOUR);
>> 10
SELECT EXTRACT(DAY FROM INTERVAL '10 23:15' DAY TO MINUTE);
>> 10
SELECT EXTRACT(DAY FROM INTERVAL '10 23:15:30' DAY TO SECOND);
>> 10
SELECT EXTRACT(HOUR FROM INTERVAL '15' HOUR);
>> 15
SELECT EXTRACT(HOUR FROM INTERVAL '2 15' DAY TO HOUR);
>> 15
SELECT EXTRACT(HOUR FROM INTERVAL '2 10:30' DAY TO MINUTE);
>> 10
SELECT EXTRACT(HOUR FROM INTERVAL '2 10:30:15' DAY TO SECOND);
>> 10
SELECT EXTRACT(HOUR FROM INTERVAL '20:10' HOUR TO MINUTE);
>> 20
SELECT EXTRACT(HOUR FROM INTERVAL '20:10:22' HOUR TO SECOND);
>> 20
SELECT EXTRACT(MINUTE FROM INTERVAL '-35' MINUTE);
>> -35
SELECT EXTRACT(MINUTE FROM INTERVAL '1 20:33' DAY TO MINUTE);
>> 33
SELECT EXTRACT(MINUTE FROM INTERVAL '1 20:33:10' DAY TO SECOND);
>> 33
SELECT EXTRACT(MINUTE FROM INTERVAL '20:34' HOUR TO MINUTE);
>> 34
SELECT EXTRACT(MINUTE FROM INTERVAL '20:34:10' HOUR TO SECOND);
>> 34
SELECT EXTRACT(MINUTE FROM INTERVAL '-34:10' MINUTE TO SECOND);
>> -34
SELECT EXTRACT(SECOND FROM INTERVAL '-100' SECOND);
>> -100
SELECT EXTRACT(SECOND FROM INTERVAL '10 11:22:33' DAY TO SECOND);
>> 33
SELECT EXTRACT(SECOND FROM INTERVAL '1:2:3' HOUR TO SECOND);
>> 3
SELECT EXTRACT(SECOND FROM INTERVAL '-2:43' MINUTE TO SECOND);
>> -43
SELECT EXTRACT(SECOND FROM INTERVAL '11.123456789' SECOND);
>> 11
SELECT EXTRACT(MILLISECOND FROM INTERVAL '11.123456789' SECOND);
>> 123
SELECT EXTRACT(MICROSECOND FROM INTERVAL '11.123456789' SECOND);
>> 123456
SELECT EXTRACT(NANOSECOND FROM INTERVAL '11.123456789' SECOND);
>> 123456789
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论