Unverified 提交 01d01c39 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #893 from katzyn/datetime

TIMESTAMP WITHOUT TIME ZONE, TIMEZONE_HOUR, and TIMEZONE_MINUTE
...@@ -2309,7 +2309,7 @@ TIME '23:59:59' ...@@ -2309,7 +2309,7 @@ TIME '23:59:59'
" "
"Other Grammar","Timestamp"," "Other Grammar","Timestamp","
TIMESTAMP 'yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]' TIMESTAMP [ WITHOUT TIME ZONE ] 'yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]'
"," ","
A timestamp literal. The limitations are the same as for the Java data type A timestamp literal. The limitations are the same as for the Java data type
""java.sql.Timestamp"", but for compatibility with other databases the suggested ""java.sql.Timestamp"", but for compatibility with other databases the suggested
...@@ -2319,7 +2319,7 @@ TIMESTAMP '2005-12-31 23:59:59' ...@@ -2319,7 +2319,7 @@ TIMESTAMP '2005-12-31 23:59:59'
" "
"Other Grammar","Timestamp with time zone"," "Other Grammar","Timestamp with time zone","
TIMESTAMP 'yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] TIMESTAMP WITH TIME ZONE 'yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]
[Z | { - | + } timeZoneOffsetString | timeZoneNameString ]' [Z | { - | + } timeZoneOffsetString | timeZoneNameString ]'
"," ","
A timestamp with time zone literal. A timestamp with time zone literal.
...@@ -2459,7 +2459,7 @@ DATE ...@@ -2459,7 +2459,7 @@ DATE
" "
"Data Types","TIMESTAMP Type"," "Data Types","TIMESTAMP Type","
{ TIMESTAMP | DATETIME | SMALLDATETIME } { TIMESTAMP [ WITHOUT TIME ZONE ] | DATETIME | SMALLDATETIME }
"," ","
The timestamp data type. The format is yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]. The timestamp data type. The format is yyyy-MM-dd hh:mm:ss[.nnnnnnnnn].
Stored internally as a BCD-encoded date, and nanoseconds since midnight. Stored internally as a BCD-encoded date, and nanoseconds since midnight.
...@@ -3697,6 +3697,7 @@ The same units as in the EXTRACT function are supported. ...@@ -3697,6 +3697,7 @@ The same units as in the EXTRACT function are supported.
This method returns a value with the same type as specified value if unit is compatible with this value. This method returns a value with the same type as specified value if unit is compatible with this value.
If specified unit is a HOUR, MINUTE, SECOND, MILLISECOND, etc and value is a DATE value DATEADD returns combined TIMESTAMP. If specified unit is a HOUR, MINUTE, SECOND, MILLISECOND, etc and value is a DATE value DATEADD returns combined TIMESTAMP.
Units DAY, MONTH, YEAR, WEEK, etc are not allowed for TIME values. Units DAY, MONTH, YEAR, WEEK, etc are not allowed for TIME values.
Units TIMEZONE_HOUR and TIMEZONE_MINUTE are only allowed for TIMESTAMP WITH TIME ZONE values.
"," ","
DATEADD('MONTH', 1, DATE '2001-01-31') DATEADD('MONTH', 1, DATE '2001-01-31')
" "
...@@ -3708,7 +3709,8 @@ Returns the the number of crossed unit boundaries between two timestamps. ...@@ -3708,7 +3709,8 @@ Returns the the number of crossed unit boundaries between two timestamps.
This method returns a long. This method returns a long.
The string indicates the unit. The string indicates the unit.
The same units as in the EXTRACT function are supported. The same units as in the EXTRACT function are supported.
If timestamps have time zone offset component it is ignored. Only TIMEZONE_HOUR and TIMEZONE_MINUTE units use the time zone offset component.
With all other units if timestamps have time zone offset component it is ignored.
"," ","
DATEDIFF('YEAR', T1.CREATED, T2.CREATED) DATEDIFF('YEAR', T1.CREATED, T2.CREATED)
" "
...@@ -3749,7 +3751,8 @@ DAY_OF_YEAR(CREATED) ...@@ -3749,7 +3751,8 @@ DAY_OF_YEAR(CREATED)
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 | EPOCH | HOUR | HH | MINUTE | MI | SECOND | SS | EPOCH
| MILLISECOND | MS | MICROSECOND | MCS | NANOSECOND | NS } | MILLISECOND | MS | MICROSECOND | MCS | NANOSECOND | NS
| TIMEZONE_HOUR | TIMEZONE_MINUTE }
FROM timestamp ) FROM timestamp )
"," ","
Returns a specific value from a timestamps. Returns a specific value from a timestamps.
......
...@@ -3137,7 +3137,8 @@ public class Parser { ...@@ -3137,7 +3137,8 @@ public class Parser {
read("FOR"); read("FOR");
Sequence sequence = readSequence(); Sequence sequence = readSequence();
r = new SequenceValue(sequence); r = new SequenceValue(sequence);
} else if (equalsToken("TIMESTAMP", name) && readIf("WITH")) { } else if (equalsToken("TIMESTAMP", name)) {
if (readIf("WITH")) {
read("TIME"); read("TIME");
read("ZONE"); read("ZONE");
if (currentTokenType != VALUE if (currentTokenType != VALUE
...@@ -3146,8 +3147,20 @@ public class Parser { ...@@ -3146,8 +3147,20 @@ public class Parser {
} }
String timestamp = currentValue.getString(); String timestamp = currentValue.getString();
read(); read();
r = ValueExpression r = ValueExpression.get(ValueTimestampTimeZone.parse(timestamp));
.get(ValueTimestampTimeZone.parse(timestamp)); } else {
if (readIf("WITHOUT")) {
read("TIME");
read("ZONE");
}
if (currentTokenType != VALUE
|| currentValue.getType() != Value.STRING) {
throw getSyntaxError();
}
String timestamp = currentValue.getString();
read();
r = ValueExpression.get(ValueTimestamp.parse(timestamp, database.getMode()));
}
} else if (currentTokenType == VALUE && } else if (currentTokenType == VALUE &&
currentValue.getType() == Value.STRING) { currentValue.getType() == Value.STRING) {
if (equalsToken("DATE", name) || if (equalsToken("DATE", name) ||
...@@ -3160,8 +3173,7 @@ public class Parser { ...@@ -3160,8 +3173,7 @@ public class Parser {
String time = currentValue.getString(); String time = currentValue.getString();
read(); read();
r = ValueExpression.get(ValueTime.parse(time)); r = ValueExpression.get(ValueTime.parse(time));
} else if (equalsToken("TIMESTAMP", name) || } else if (equalsToken("TS", name)) {
equalsToken("TS", name)) {
String timestamp = currentValue.getString(); String timestamp = currentValue.getString();
read(); read();
r = ValueExpression r = ValueExpression
...@@ -4340,6 +4352,10 @@ public class Parser { ...@@ -4340,6 +4352,10 @@ public class Parser {
read("ZONE"); read("ZONE");
original += " WITH TIME ZONE"; original += " WITH TIME ZONE";
} }
} else if (readIf("WITHOUT")) {
read("TIME");
read("ZONE");
original += " WITHOUT TIME ZONE";
} }
} else { } else {
regular = true; regular = true;
......
...@@ -112,7 +112,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -112,7 +112,8 @@ public class Function extends Expression implements FunctionCall {
/** /**
* Pseudo functions for DATEADD, DATEDIFF, and EXTRACT. * Pseudo functions for DATEADD, DATEDIFF, and EXTRACT.
*/ */
public static final int MILLISECOND = 126, EPOCH = 127, MICROSECOND = 128, NANOSECOND = 129; public static final int MILLISECOND = 126, EPOCH = 127, MICROSECOND = 128, NANOSECOND = 129,
TIMEZONE_HOUR = 130, TIMEZONE_MINUTE = 131;
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,
...@@ -209,6 +210,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -209,6 +210,8 @@ public class Function extends Expression implements FunctionCall {
DATE_PART.put("MCS", MICROSECOND); DATE_PART.put("MCS", MICROSECOND);
DATE_PART.put("NANOSECOND", NANOSECOND); DATE_PART.put("NANOSECOND", NANOSECOND);
DATE_PART.put("NS", NANOSECOND); DATE_PART.put("NS", NANOSECOND);
DATE_PART.put("TIMEZONE_HOUR", TIMEZONE_HOUR);
DATE_PART.put("TIMEZONE_MINUTE", TIMEZONE_MINUTE);
// SOUNDEX_INDEX // SOUNDEX_INDEX
String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R"; String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R";
...@@ -1921,6 +1924,16 @@ public class Function extends Expression implements FunctionCall { ...@@ -1921,6 +1924,16 @@ public class Function extends Expression implements FunctionCall {
break; break;
case NANOSECOND: case NANOSECOND:
break; break;
case TIMEZONE_HOUR:
count *= 60;
//$FALL-THROUGH$
case TIMEZONE_MINUTE: {
if (!(v instanceof ValueTimestampTimeZone)) {
throw DbException.getUnsupportedException("DATEADD " + part);
}
count += ((ValueTimestampTimeZone) v).getTimeZoneOffsetMins();
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, timeNanos, (short) count);
}
default: default:
throw DbException.getUnsupportedException("DATEADD " + part); throw DbException.getUnsupportedException("DATEADD " + part);
} }
...@@ -2013,6 +2026,26 @@ public class Function extends Expression implements FunctionCall { ...@@ -2013,6 +2026,26 @@ public class Function extends Expression implements FunctionCall {
- (DateTimeUtils.monthFromDateValue(dateValue1) - 1) / 3; - (DateTimeUtils.monthFromDateValue(dateValue1) - 1) / 3;
case YEAR: case YEAR:
return DateTimeUtils.yearFromDateValue(dateValue2) - DateTimeUtils.yearFromDateValue(dateValue1); return DateTimeUtils.yearFromDateValue(dateValue2) - DateTimeUtils.yearFromDateValue(dateValue1);
case TIMEZONE_HOUR:
case TIMEZONE_MINUTE: {
int offsetMinutes1;
if (v1 instanceof ValueTimestampTimeZone) {
offsetMinutes1 = ((ValueTimestampTimeZone) v1).getTimeZoneOffsetMins();
} else {
offsetMinutes1 = DateTimeUtils.getTimeZoneOffsetMillis(null, dateValue1, a1[1]);
}
int offsetMinutes2;
if (v2 instanceof ValueTimestampTimeZone) {
offsetMinutes2 = ((ValueTimestampTimeZone) v2).getTimeZoneOffsetMins();
} else {
offsetMinutes2 = DateTimeUtils.getTimeZoneOffsetMillis(null, dateValue2, a2[1]);
}
if (field == TIMEZONE_HOUR) {
return (offsetMinutes2 / 60) - (offsetMinutes1 / 60);
} else {
return offsetMinutes2 - offsetMinutes1;
}
}
default: default:
throw DbException.getUnsupportedException("DATEDIFF " + part); throw DbException.getUnsupportedException("DATEDIFF " + part);
} }
...@@ -2886,39 +2919,52 @@ public class Function extends Expression implements FunctionCall { ...@@ -2886,39 +2919,52 @@ public class Function extends Expression implements FunctionCall {
long dateValue = a[0]; long dateValue = a[0];
long timeNanos = a[1]; long timeNanos = a[1];
switch (field) { switch (field) {
case Function.YEAR: case YEAR:
return DateTimeUtils.yearFromDateValue(dateValue); return DateTimeUtils.yearFromDateValue(dateValue);
case Function.MONTH: case MONTH:
return DateTimeUtils.monthFromDateValue(dateValue); return DateTimeUtils.monthFromDateValue(dateValue);
case Function.DAY_OF_MONTH: case DAY_OF_MONTH:
return DateTimeUtils.dayFromDateValue(dateValue); return DateTimeUtils.dayFromDateValue(dateValue);
case Function.HOUR: case HOUR:
return (int) (timeNanos / 3_600_000_000_000L % 24); return (int) (timeNanos / 3_600_000_000_000L % 24);
case Function.MINUTE: case MINUTE:
return (int) (timeNanos / 60_000_000_000L % 60); return (int) (timeNanos / 60_000_000_000L % 60);
case Function.SECOND: case SECOND:
return (int) (timeNanos / 1_000_000_000 % 60); return (int) (timeNanos / 1_000_000_000 % 60);
case Function.MILLISECOND: case MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000); return (int) (timeNanos / 1_000_000 % 1_000);
case Function.MICROSECOND: case MICROSECOND:
return (int) (timeNanos / 1_000 % 1_000_000); return (int) (timeNanos / 1_000 % 1_000_000);
case Function.NANOSECOND: case NANOSECOND:
return (int) (timeNanos % 1_000_000_000); return (int) (timeNanos % 1_000_000_000);
case Function.DAY_OF_YEAR: case DAY_OF_YEAR:
return DateTimeUtils.getDayOfYear(dateValue); return DateTimeUtils.getDayOfYear(dateValue);
case Function.DAY_OF_WEEK: case DAY_OF_WEEK:
return DateTimeUtils.getSundayDayOfWeek(dateValue); return DateTimeUtils.getSundayDayOfWeek(dateValue);
case Function.WEEK: case WEEK:
GregorianCalendar gc = DateTimeUtils.getCalendar(); GregorianCalendar gc = DateTimeUtils.getCalendar();
return DateTimeUtils.getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek()); return DateTimeUtils.getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek());
case Function.QUARTER: case QUARTER:
return (DateTimeUtils.monthFromDateValue(dateValue) - 1) / 3 + 1; return (DateTimeUtils.monthFromDateValue(dateValue) - 1) / 3 + 1;
case Function.ISO_YEAR: case ISO_YEAR:
return DateTimeUtils.getIsoWeekYear(dateValue); return DateTimeUtils.getIsoWeekYear(dateValue);
case Function.ISO_WEEK: case ISO_WEEK:
return DateTimeUtils.getIsoWeekOfYear(dateValue); return DateTimeUtils.getIsoWeekOfYear(dateValue);
case Function.ISO_DAY_OF_WEEK: case ISO_DAY_OF_WEEK:
return DateTimeUtils.getIsoDayOfWeek(dateValue); 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;
}
} }
throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')'); throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
} }
......
...@@ -302,7 +302,8 @@ public class DataType { ...@@ -302,7 +302,8 @@ public class DataType {
add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp", add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp",
createDate(ValueTimestamp.PRECISION, "TIMESTAMP", createDate(ValueTimestamp.PRECISION, "TIMESTAMP",
ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE), ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE),
new String[]{"TIMESTAMP", "DATETIME", "DATETIME2", "SMALLDATETIME"}, new String[]{"TIMESTAMP", "TIMESTAMP WITHOUT TIME ZONE",
"DATETIME", "DATETIME2", "SMALLDATETIME"},
// 24 for ValueTimestamp, 32 for java.sql.Timestamp // 24 for ValueTimestamp, 32 for java.sql.Timestamp
56 56
); );
......
...@@ -2,3 +2,26 @@ ...@@ -2,3 +2,26 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html). -- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
CREATE TABLE TEST(T1 TIMESTAMP, T2 TIMESTAMP WITHOUT TIME ZONE);
> ok
INSERT INTO TEST(T1, T2) VALUES (TIMESTAMP '2010-01-01 10:00:00', TIMESTAMP WITHOUT TIME ZONE '2010-01-01 10:00:00');
> update count: 1
SELECT T1, T2, T1 = T2 FROM TEST;
> T1 T2 T1 = T2
> --------------------- --------------------- -------
> 2010-01-01 10:00:00.0 2010-01-01 10:00:00.0 TRUE
> rows: 1
SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST' ORDER BY ORDINAL_POSITION;
> COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_TYPE
> ----------- --------- --------- ---------------------------
> T1 93 TIMESTAMP TIMESTAMP
> T2 93 TIMESTAMP TIMESTAMP WITHOUT TIME ZONE
> rows (ordered): 2
DROP TABLE TEST;
> ok
...@@ -149,3 +149,15 @@ SELECT TIMESTAMPADD('DAY', 10, TIMESTAMP '2000-01-05 15:00:30.123456789'); ...@@ -149,3 +149,15 @@ SELECT TIMESTAMPADD('DAY', 10, TIMESTAMP '2000-01-05 15:00:30.123456789');
> ----------------------------------------- > -----------------------------------------
> 2000-01-15 15:00:30.123456789 > 2000-01-15 15:00:30.123456789
> rows: 1 > rows: 1
SELECT TIMESTAMPADD('TIMEZONE_HOUR', 1, TIMESTAMP WITH TIME ZONE '2010-01-01 10:00:00+07:30') AS T;
> T
> ---------------------------
> 2010-01-01 10:00:00.0+08:30
> rows: 1
SELECT TIMESTAMPADD('TIMEZONE_MINUTE', -45, TIMESTAMP WITH TIME ZONE '2010-01-01 10:00:00+07:30') AS T;
> T
> ---------------------------
> 2010-01-01 10:00:00.0+06:45
> rows: 1
...@@ -241,3 +241,17 @@ SELECT DATEDIFF('QUARTER', DATE '-1000-01-01', DATE '2000-01-01'); ...@@ -241,3 +241,17 @@ SELECT DATEDIFF('QUARTER', DATE '-1000-01-01', DATE '2000-01-01');
> ----- > -----
> 12000 > 12000
> rows: 1 > rows: 1
SELECT DATEDIFF('TIMEZONE_HOUR', TIMESTAMP WITH TIME ZONE '2010-01-01 10:00:00+01',
TIMESTAMP WITH TIME ZONE '2012-02-02 12:00:00+02');
> 1
> -
> 1
> rows: 1
SELECT DATEDIFF('TIMEZONE_MINUTE', TIMESTAMP WITH TIME ZONE '2010-01-01 10:00:00+01:15',
TIMESTAMP WITH TIME ZONE '2012-02-02 12:00:00+02');
> 45
> --
> 45
> rows: 1
...@@ -55,3 +55,27 @@ select EXTRACT (EPOCH from timestamp with time zone '2000-01-03 12:00:00.123456+ ...@@ -55,3 +55,27 @@ 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(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00+07:15');
> 7
> -
> 7
> rows: 1
SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00-08:30');
> -8
> --
> -8
> rows: 1
SELECT EXTRACT(TIMEZONE_MINUTE FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00+07:15');
> 15
> --
> 15
> rows: 1
SELECT EXTRACT(TIMEZONE_MINUTE FROM TIMESTAMP WITH TIME ZONE '2010-01-02 5:00:00-08:30');
> -30
> ---
> -30
> rows: 1
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论