Unverified 提交 2315bd35 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #872 from katzyn/datetime

Assorted date-time related changes
...@@ -3727,7 +3727,8 @@ DAY_OF_YEAR(CREATED) ...@@ -3727,7 +3727,8 @@ DAY_OF_YEAR(CREATED)
"Functions (Time and Date)","EXTRACT"," "Functions (Time and Date)","EXTRACT","
EXTRACT ( { YEAR | YY | MONTH | MM | QUARTER | WEEK | ISO_WEEK EXTRACT ( { YEAR | YY | MONTH | MM | QUARTER | WEEK | ISO_WEEK
| DAY | DD | DAY_OF_YEAR | DOY | DAY | DD | DAY_OF_YEAR | DOY
| HOUR | HH | MINUTE | MI | SECOND | SS | MILLISECOND | MS } | HOUR | HH | MINUTE | MI | SECOND | SS | EPOCH
| MILLISECOND | MS | MICROSECOND | MCS | NANOSECOND | NS }
FROM timestamp ) FROM timestamp )
"," ","
Returns a specific value from a timestamps. Returns a specific value from a timestamps.
......
...@@ -111,14 +111,9 @@ public class Function extends Expression implements FunctionCall { ...@@ -111,14 +111,9 @@ public class Function extends Expression implements FunctionCall {
ISO_WEEK = 124, ISO_DAY_OF_WEEK = 125; ISO_WEEK = 124, ISO_DAY_OF_WEEK = 125;
/** /**
* Pseudo function for {@code EXTRACT(MILLISECOND FROM ...)}. * Pseudo functions for DATEADD, DATEDIFF, and EXTRACT.
*/ */
public static final int MILLISECOND = 126; public static final int MILLISECOND = 126, EPOCH = 127, MICROSECOND = 128, NANOSECOND = 129;
/**
* Pseudo function for {@code EXTRACT(EPOCH FROM ...)}.
*/
public static final int EPOCH = 127;
public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152, public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152,
IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155, IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155,
...@@ -206,6 +201,10 @@ public class Function extends Expression implements FunctionCall { ...@@ -206,6 +201,10 @@ public class Function extends Expression implements FunctionCall {
DATE_PART.put("MILLISECOND", MILLISECOND); DATE_PART.put("MILLISECOND", MILLISECOND);
DATE_PART.put("MS", MILLISECOND); DATE_PART.put("MS", MILLISECOND);
DATE_PART.put("EPOCH", EPOCH); DATE_PART.put("EPOCH", EPOCH);
DATE_PART.put("MICROSECOND", MICROSECOND);
DATE_PART.put("MCS", MICROSECOND);
DATE_PART.put("NANOSECOND", NANOSECOND);
DATE_PART.put("NS", NANOSECOND);
// SOUNDEX_INDEX // SOUNDEX_INDEX
String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R"; String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R";
...@@ -864,7 +863,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -864,7 +863,7 @@ public class Function extends Expression implements FunctionCall {
case SECOND: case SECOND:
case WEEK: case WEEK:
case YEAR: case YEAR:
result = ValueInt.get(getDatePart(v0, info.type)); result = ValueInt.get(getIntDatePart(v0, info.type));
break; break;
case MONTH_NAME: { case MONTH_NAME: {
SimpleDateFormat monthName = new SimpleDateFormat("MMMM", SimpleDateFormat monthName = new SimpleDateFormat("MMMM",
...@@ -1502,12 +1501,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -1502,12 +1501,8 @@ public class Function extends Expression implements FunctionCall {
break; break;
case EXTRACT: { case EXTRACT: {
int field = getDatePart(v0.getString()); int field = getDatePart(v0.getString());
// Normal case when we don't retrieve the EPOCH time
if (field != EPOCH) { if (field != EPOCH) {
result = ValueInt.get(getIntDatePart(v1, field));
result = ValueInt.get(getDatePart(v1, field));
} else { } else {
// Case where we retrieve the EPOCH time. // Case where we retrieve the EPOCH time.
...@@ -1866,8 +1861,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1866,8 +1861,7 @@ public class Function extends Expression implements FunctionCall {
private static Value dateadd(String part, long count, Value v) { private static Value dateadd(String part, long count, Value v) {
int field = getDatePart(part); int field = getDatePart(part);
//v = v.convertTo(Value.TIMESTAMP); if (field != MILLISECOND && field != MICROSECOND && field != NANOSECOND &&
if (field != MILLISECOND &&
(count > Integer.MAX_VALUE || count < Integer.MIN_VALUE)) { (count > Integer.MAX_VALUE || count < Integer.MIN_VALUE)) {
throw DbException.getInvalidValueException("DATEADD count", count); throw DbException.getInvalidValueException("DATEADD count", count);
} }
...@@ -1917,11 +1911,17 @@ public class Function extends Expression implements FunctionCall { ...@@ -1917,11 +1911,17 @@ public class Function extends Expression implements FunctionCall {
count *= 60_000_000_000L; count *= 60_000_000_000L;
break; break;
case SECOND: case SECOND:
case EPOCH:
count *= 1_000_000_000; count *= 1_000_000_000;
break; break;
case MILLISECOND: case MILLISECOND:
count *= 1_000_000; count *= 1_000_000;
break; break;
case MICROSECOND:
count *= 1_000;
break;
case NANOSECOND:
break;
default: default:
throw DbException.getUnsupportedException("DATEADD " + part); throw DbException.getUnsupportedException("DATEADD " + part);
} }
...@@ -1966,17 +1966,27 @@ public class Function extends Expression implements FunctionCall { ...@@ -1966,17 +1966,27 @@ public class Function extends Expression implements FunctionCall {
long dateValue2 = a2[0]; long dateValue2 = a2[0];
long absolute2 = DateTimeUtils.absoluteDayFromDateValue(dateValue2); long absolute2 = DateTimeUtils.absoluteDayFromDateValue(dateValue2);
switch (field) { switch (field) {
case NANOSECOND:
case MICROSECOND:
case MILLISECOND: case MILLISECOND:
case SECOND: case SECOND:
case EPOCH:
case MINUTE: case MINUTE:
case HOUR: case HOUR:
long timeNanos1 = a1[1]; long timeNanos1 = a1[1];
long timeNanos2 = a2[1]; long timeNanos2 = a2[1];
switch (field) { switch (field) {
case NANOSECOND:
return (absolute2 - absolute1) * DateTimeUtils.NANOS_PER_DAY
+ (timeNanos2 - timeNanos1);
case MICROSECOND:
return (absolute2 - absolute1) * (DateTimeUtils.MILLIS_PER_DAY * 1_000)
+ (timeNanos2 / 1_000 - timeNanos1 / 1_000);
case MILLISECOND: case MILLISECOND:
return (absolute2 - absolute1) * DateTimeUtils.MILLIS_PER_DAY return (absolute2 - absolute1) * DateTimeUtils.MILLIS_PER_DAY
+ (timeNanos2 / 1_000_000 - timeNanos1 / 1_000_000); + (timeNanos2 / 1_000_000 - timeNanos1 / 1_000_000);
case SECOND: case SECOND:
case EPOCH:
return (absolute2 - absolute1) * 86_400 return (absolute2 - absolute1) * 86_400
+ (timeNanos2 / 1_000_000_000 - timeNanos1 / 1_000_000_000); + (timeNanos2 / 1_000_000_000 - timeNanos1 / 1_000_000_000);
case MINUTE: case MINUTE:
...@@ -2859,7 +2869,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -2859,7 +2869,7 @@ public class Function extends Expression implements FunctionCall {
* @param field the field type, see {@link Function} for constants * @param field the field type, see {@link Function} for constants
* @return the value * @return the value
*/ */
public static int getDatePart(Value date, int field) { public static int getIntDatePart(Value date, int field) {
long[] a = DateTimeUtils.dateAndTimeFromValue(date); long[] a = DateTimeUtils.dateAndTimeFromValue(date);
long dateValue = a[0]; long dateValue = a[0];
long timeNanos = a[1]; long timeNanos = a[1];
...@@ -2878,6 +2888,10 @@ public class Function extends Expression implements FunctionCall { ...@@ -2878,6 +2888,10 @@ public class Function extends Expression implements FunctionCall {
return (int) (timeNanos / 1_000_000_000 % 60); return (int) (timeNanos / 1_000_000_000 % 60);
case Function.MILLISECOND: case Function.MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000); return (int) (timeNanos / 1_000_000 % 1_000);
case Function.MICROSECOND:
return (int) (timeNanos / 1_000 % 1_000_000);
case Function.NANOSECOND:
return (int) (timeNanos % 1_000_000_000);
case Function.DAY_OF_YEAR: case Function.DAY_OF_YEAR:
return DateTimeUtils.getDayOfYear(dateValue); return DateTimeUtils.getDayOfYear(dateValue);
case Function.DAY_OF_WEEK: case Function.DAY_OF_WEEK:
......
...@@ -921,19 +921,6 @@ public class DateTimeUtils { ...@@ -921,19 +921,6 @@ public class DateTimeUtils {
return new Date(millis); return new Date(millis);
} }
/**
* Convert an encoded date value to millis, using the supplied timezone.
*
* @param tz the timezone
* @param dateValue the date value
* @return the date
*/
public static long convertDateValueToMillis(TimeZone tz, long dateValue) {
return getMillis(tz, yearFromDateValue(dateValue),
monthFromDateValue(dateValue), dayFromDateValue(dateValue), 0,
0, 0, 0);
}
/** /**
* Convert an encoded date-time value to millis, using the supplied timezone. * Convert an encoded date-time value to millis, using the supplied timezone.
* *
......
...@@ -219,34 +219,31 @@ public class ValueTimestampTimeZone extends Value { ...@@ -219,34 +219,31 @@ public class ValueTimestampTimeZone extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueTimestampTimeZone t = (ValueTimestampTimeZone) o; ValueTimestampTimeZone t = (ValueTimestampTimeZone) o;
// We are pretending that the dateValue is in UTC because that gives us // Maximum time zone offset is +/-18 hours so difference in days between local
// a stable sort even if the DST database changes. // and UTC cannot be more than one day
long daysA = DateTimeUtils.absoluteDayFromDateValue(dateValue);
// convert to minutes and add timezone offset long timeA = timeNanos - timeZoneOffsetMins * 60_000_000_000L;
long a = DateTimeUtils.convertDateValueToMillis( if (timeA < 0) {
DateTimeUtils.UTC, dateValue) / timeA += DateTimeUtils.NANOS_PER_DAY;
(1000L * 60L); daysA--;
long ma = timeNanos / (1000L * 1000L * 1000L * 60L); } else if (timeA >= DateTimeUtils.NANOS_PER_DAY) {
a += ma; timeA -= DateTimeUtils.NANOS_PER_DAY;
a -= timeZoneOffsetMins; daysA++;
}
// convert to minutes and add timezone offset long daysB = DateTimeUtils.absoluteDayFromDateValue(t.dateValue);
long b = DateTimeUtils.convertDateValueToMillis( long timeB = t.timeNanos - t.timeZoneOffsetMins * 60_000_000_000L;
DateTimeUtils.UTC, t.dateValue) / if (timeB < 0) {
(1000L * 60L); timeB += DateTimeUtils.NANOS_PER_DAY;
long mb = t.timeNanos / (1000L * 1000L * 1000L * 60L); daysB--;
b += mb; } else if (timeB >= DateTimeUtils.NANOS_PER_DAY) {
b -= t.timeZoneOffsetMins; timeB -= DateTimeUtils.NANOS_PER_DAY;
daysB++;
// compare date }
int c = Long.compare(a, b); int cmp = Long.compare(daysA, daysB);
if (c != 0) { if (cmp != 0) {
return c; return cmp;
} }
// compare time return Long.compare(timeA, timeB);
long na = timeNanos - (ma * 1000L * 1000L * 1000L * 60L);
long nb = t.timeNanos - (mb * 1000L * 1000L * 1000L * 60L);
return Long.compare(na, nb);
} }
@Override @Override
......
...@@ -105,6 +105,18 @@ call dateadd('MS', 1, TIMESTAMP '2001-02-03 04:05:06.789001'); ...@@ -105,6 +105,18 @@ call dateadd('MS', 1, TIMESTAMP '2001-02-03 04:05:06.789001');
> 2001-02-03 04:05:06.790001 > 2001-02-03 04:05:06.790001
> rows: 1 > rows: 1
SELECT DATEADD('MICROSECOND', 1, TIME '10:00:01'), DATEADD('MCS', 1, TIMESTAMP '2010-10-20 10:00:01.1');
> TIME '10:00:01.000001' TIMESTAMP '2010-10-20 10:00:01.100001'
> ---------------------- --------------------------------------
> 10:00:01.000001 2010-10-20 10:00:01.100001
> rows: 1
SELECT DATEADD('NANOSECOND', 1, TIME '10:00:01'), DATEADD('NS', 1, TIMESTAMP '2010-10-20 10:00:01.1');
> TIME '10:00:01.000000001' TIMESTAMP '2010-10-20 10:00:01.100000001'
> ------------------------- -----------------------------------------
> 10:00:01.000000001 2010-10-20 10:00:01.100000001
> rows: 1
SELECT DATEADD('HOUR', 1, DATE '2010-01-20'); SELECT DATEADD('HOUR', 1, DATE '2010-01-20');
> TIMESTAMP '2010-01-20 01:00:00.0' > TIMESTAMP '2010-01-20 01:00:00.0'
> --------------------------------- > ---------------------------------
......
...@@ -159,6 +159,22 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0 ...@@ -159,6 +159,22 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0
> 3408134399000 > 3408134399000
> rows: 1 > rows: 1
SELECT DATEDIFF('MICROSECOND', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('MCS', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('MCS', '2006-01-01 00:00:00.0000000', '2006-01-02 00:00:00.123456789');
> 123456 123456 86400123456
> ------ ------ -----------
> 123456 123456 86400123456
> rows: 1
SELECT DATEDIFF('NANOSECOND', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('NS', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('NS', '2006-01-01 00:00:00.0000000', '2006-01-02 00:00:00.123456789');
> 123456789 123456789 86400123456789
> --------- --------- --------------
> 123456789 123456789 86400123456789
> rows: 1
SELECT DATEDIFF('WEEK', DATE '2018-02-02', DATE '2018-02-03'), DATEDIFF('ISO_WEEK', DATE '2018-02-02', DATE '2018-02-03'); SELECT DATEDIFF('WEEK', DATE '2018-02-02', DATE '2018-02-03'), DATEDIFF('ISO_WEEK', DATE '2018-02-02', DATE '2018-02-03');
> 0 0 > 0 0
> - - > - -
......
...@@ -3,6 +3,20 @@ ...@@ -3,6 +3,20 @@
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
SELECT EXTRACT (MICROSECOND FROM TIME '10:00:00.123456789'),
EXTRACT (MCS FROM TIMESTAMP '2015-01-01 11:22:33.987654321');
> 123456 987654
> ------ ------
> 123456 987654
> rows: 1
SELECT EXTRACT (NANOSECOND FROM TIME '10:00:00.123456789'),
EXTRACT (NS FROM TIMESTAMP '2015-01-01 11:22:33.987654321');
> 123456789 987654321
> --------- ---------
> 123456789 987654321
> rows: 1
select EXTRACT (EPOCH from time '00:00:00'); select EXTRACT (EPOCH from time '00:00:00');
> 0 > 0
......
...@@ -126,21 +126,27 @@ public class TestTimeStampWithTimeZone extends TestBase { ...@@ -126,21 +126,27 @@ public class TestTimeStampWithTimeZone extends TestBase {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-01 12:00:00.00+00:15"); ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-01 12:00:00.00+00:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 12:00:01.00+01:15"); ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 12:00:01.00+01:15");
int c = a.compareTo(b, null); int c = a.compareTo(b, null);
assertEquals(c, 1); assertEquals(1, c);
c = b.compareTo(a, null);
assertEquals(-1, c);
} }
private void test3() { private void test3() {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:02.00+01:15"); ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:02.00+01:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15"); ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15");
int c = a.compareTo(b, null); int c = a.compareTo(b, null);
assertEquals(c, 1); assertEquals(1, c);
c = b.compareTo(a, null);
assertEquals(-1, c);
} }
private void test4() { private void test4() {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:01.00+01:15"); ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:01.00+01:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15"); ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15");
int c = a.compareTo(b, null); int c = a.compareTo(b, null);
assertEquals(c, 0); assertEquals(0, c);
c = b.compareTo(a, null);
assertEquals(0, c);
} }
private void test5() throws SQLException { private void test5() throws SQLException {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论