[CALCITE-6138] Parser does not accept TIMESTAMP WITH TIME ZONE as a data type
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
diff --git a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
index aeb749a..117c79a 100644
--- a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
+++ b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
@@ -36,6 +36,7 @@
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -192,6 +193,9 @@
private static Object literalValue(RexLiteral literal) {
Comparable<?> value = RexLiteral.value(literal);
switch (literal.getTypeName()) {
+ case TIMESTAMP_TZ:
+ assert value instanceof TimestampWithTimeZoneString;
+ return value.toString();
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
assert value instanceof TimestampString;
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 78f9f59..542b164 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -4787,6 +4787,7 @@
{
final String p;
final Span s;
+ boolean local = false;
}
{
<LBRACE_D> <QUOTED_STRING> {
@@ -4818,6 +4819,7 @@
return SqlLiteral.createUnknown("DATETIME", p, s.end(this));
}
|
+ LOOKAHEAD(2)
<TIME> { s = span(); } p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIME", p, s.end(this));
}
@@ -4827,8 +4829,14 @@
return SqlLiteral.createUnknown("TIMESTAMP", p, s.end(this));
}
|
- <TIMESTAMP> { s = span(); } <WITH> <LOCAL> <TIME> <ZONE> p = SimpleStringLiteral() {
- return SqlLiteral.createUnknown("TIMESTAMP WITH LOCAL TIME ZONE", p, s.end(this));
+ LOOKAHEAD(2)
+ <TIME> { s = span(); } <WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> p = SimpleStringLiteral() {
+ return SqlLiteral.createUnknown("TIME WITH " + (local ? "LOCAL " : "") + "TIME ZONE", p, s.end(this));
+ }
+|
+ LOOKAHEAD(2)
+ <TIMESTAMP> { s = span(); } <WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> p = SimpleStringLiteral() {
+ return SqlLiteral.createUnknown("TIMESTAMP WITH " + (local ? "LOCAL " : "") + "TIME ZONE", p, s.end(this));
}
}
@@ -6112,7 +6120,6 @@
{
int precision = -1;
SqlTypeName typeName;
- boolean withLocalTimeZone = false;
final Span s;
}
{
@@ -6124,25 +6131,15 @@
LOOKAHEAD(2)
<TIME> { s = span(); }
precision = PrecisionOpt()
- withLocalTimeZone = TimeZoneOpt()
+ typeName = TimeZoneOpt(true)
{
- if (withLocalTimeZone) {
- typeName = SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE;
- } else {
- typeName = SqlTypeName.TIME;
- }
return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
}
|
<TIMESTAMP> { s = span(); }
precision = PrecisionOpt()
- withLocalTimeZone = TimeZoneOpt()
+ typeName = TimeZoneOpt(false)
{
- if (withLocalTimeZone) {
- typeName = SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE;
- } else {
- typeName = SqlTypeName.TIMESTAMP;
- }
return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
}
}
@@ -6163,23 +6160,22 @@
/**
* Parse a time zone suffix for DateTime types. According to SQL-2011,
-* "with time zone" and "without time zone" belong to standard SQL but we
-* only implement the "without time zone".
-*
-* <p>We also support "with local time zone".
-*
-* @return true if this is "with local time zone".
+* "with time zone" and "without time zone" belong to standard SQL.
+* We also support "with local time zone".
*/
-boolean TimeZoneOpt() :
+SqlTypeName TimeZoneOpt(boolean timeType) :
{
+ boolean local = false;
}
{
LOOKAHEAD(3)
- <WITHOUT> <TIME> <ZONE> { return false; }
+ <WITHOUT> <TIME> <ZONE> { return timeType ? SqlTypeName.TIME : SqlTypeName.TIMESTAMP; }
|
- <WITH> <LOCAL> <TIME> <ZONE> { return true; }
+ <WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> {
+ return timeType ? (local ? SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE : SqlTypeName.TIME_TZ)
+ : (local ? SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE : SqlTypeName.TIMESTAMP_TZ); }
|
- { return false; }
+ { return timeType ? SqlTypeName.TIME : SqlTypeName.TIMESTAMP; }
}
/**
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index e37120e..1bc88f1 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -2451,6 +2451,7 @@
// Search does not include TZ so this conversion is okay
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
expr = Expressions.call(BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method, expr);
break;
default:
@@ -2633,6 +2634,7 @@
translator.getRoot()));
// fall through
case TIMESTAMP:
+ case TIMESTAMP_TZ:
type = long.class;
floorMethod = custom ? customTimestampMethod : timestampMethod;
preFloor = true;
@@ -2713,6 +2715,7 @@
final Expression operand2 = argValueList.get(2);
switch (call.getType().getSqlTypeName()) {
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
case TIMESTAMP:
return Expressions.call(customTimestampMethod, translator.getRoot(),
operand0, operand1, operand2);
@@ -2745,6 +2748,7 @@
private Method getMethod(RexCall call) {
switch (call.operands.get(1).getType().getSqlTypeName()) {
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
case TIMESTAMP:
return customTimestampMethod;
default:
@@ -3106,6 +3110,7 @@
Expressions.call(BuiltInMethod.TIME_ZONE.method,
translator.getRoot()));
// fall through
+ case TIMESTAMP_TZ:
case TIMESTAMP:
operand =
Expressions.call(BuiltInMethod.FLOOR_DIV.method, operand,
@@ -3136,6 +3141,7 @@
Expressions.constant(TimeUnit.DAY.multiplier.longValue()));
// fall through
case TIMESTAMP:
+ case TIMESTAMP_TZ:
// convert to seconds
return Expressions.divide(operand,
Expressions.constant(TimeUnit.SECOND.multiplier.longValue()));
diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
index 05ed581..1b015fe 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
@@ -180,6 +180,7 @@
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case INTEGER:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
@@ -187,6 +188,7 @@
return type.isNullable() ? Integer.class : int.class;
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
case BIGINT:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
index 903a65f..f2d2c6a 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
@@ -302,6 +302,7 @@
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -311,6 +312,7 @@
case FLOAT: // sic
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
@@ -364,6 +366,7 @@
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -373,6 +376,7 @@
case DOUBLE:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
index 9c51470..d720624 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
@@ -575,7 +575,14 @@
ts = TimestampString.fromCalendarFields(calendar);
p = operand.getType().getPrecision();
return rexBuilder.makeTimestampLiteral(ts, p);
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ: {
+ ts = TimestampString.fromCalendarFields(calendar);
+ final TimeZone tz = calendar.getTimeZone();
+ final TimestampWithTimeZoneString localTs = new TimestampWithTimeZoneString(ts, tz);
+ p = operand.getType().getPrecision();
+ return rexBuilder.makeTimestampTzLiteral(localTs, p);
+ }
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE: {
ts = TimestampString.fromCalendarFields(calendar);
final TimeZone tz = TimeZone.getTimeZone(this.timeZone);
final TimestampString localTs =
@@ -584,6 +591,7 @@
.getLocalTimestampString();
p = operand.getType().getPrecision();
return rexBuilder.makeTimestampWithLocalTimeZoneLiteral(localTs, p);
+ }
case DATE:
final DateString d = DateString.fromCalendarFields(calendar);
return rexBuilder.makeDateLiteral(d);
@@ -646,6 +654,12 @@
private Calendar timestampValue(RexLiteral timeLiteral) {
switch (timeLiteral.getTypeName()) {
+ case TIMESTAMP_TZ:
+ TimestampWithTimeZoneString value =
+ requireNonNull(timeLiteral.getValueAs(TimestampWithTimeZoneString.class));
+ return Util.calendar(
+ value.getLocalTimestampString().getMillisSinceEpoch(),
+ value.getTimeZone());
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
final TimeZone tz = TimeZone.getTimeZone(this.timeZone);
return Util.calendar(
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
index 289c088..579efc333 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
@@ -775,7 +775,8 @@
exprsLineage.put(expr, i);
SqlTypeName sqlTypeName = expr.getType().getSqlTypeName();
if (sqlTypeName == SqlTypeName.TIMESTAMP
- || sqlTypeName == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+ || sqlTypeName == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE
+ || sqlTypeName == SqlTypeName.TIMESTAMP_TZ) {
timestampExprs.add(expr);
}
}
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
index 55702ce..d4a9cac 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
@@ -101,10 +101,12 @@
return 15;
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case DATE:
return 0; // SQL99 part 2 section 6.1 syntax rule 30
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
// farrago supports only 0 (see
// SqlTypeName.getDefaultPrecision), but it should be 6
// (microseconds) per SQL99 part 2 section 6.1 syntax rule 30.
@@ -126,8 +128,10 @@
return 65536;
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
return SqlTypeName.MAX_DATETIME_PRECISION;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
@@ -168,6 +172,8 @@
return isPrefix ? "TIMESTAMP '" : "'";
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return isPrefix ? "TIMESTAMP WITH LOCAL TIME ZONE '" : "'";
+ case TIMESTAMP_TZ:
+ return isPrefix ? "TIMESTAMP WITH TIME ZONE '" : "'";
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
@@ -187,6 +193,8 @@
return isPrefix ? "TIME '" : "'";
case TIME_WITH_LOCAL_TIME_ZONE:
return isPrefix ? "TIME WITH LOCAL TIME ZONE '" : "'";
+ case TIME_TZ:
+ return isPrefix ? "TIME WITH TIME ZONE '" : "'";
case DATE:
return isPrefix ? "DATE '" : "'";
case ARRAY:
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index dab524f..ecbe67e 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -48,7 +48,9 @@
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Sarg;
import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -1015,6 +1017,14 @@
}
o = ((TimeString) o).round(p);
break;
+ case TIME_TZ:
+ assert o instanceof TimeWithTimeZoneString;
+ p = type.getPrecision();
+ if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
+ p = 0;
+ }
+ o = ((TimeWithTimeZoneString) o).round(p);
+ break;
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
assert o instanceof TimestampString;
@@ -1024,6 +1034,14 @@
}
o = ((TimestampString) o).round(p);
break;
+ case TIMESTAMP_TZ:
+ assert o instanceof TimestampWithTimeZoneString;
+ p = type.getPrecision();
+ if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
+ p = 0;
+ }
+ o = ((TimestampWithTimeZoneString) o).round(p);
+ break;
default:
break;
}
@@ -1270,6 +1288,16 @@
SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
}
+ /** Creates a Time with time-zone literal. */
+ public RexLiteral makeTimeTzLiteral(
+ TimeWithTimeZoneString time,
+ int precision) {
+ return makeLiteral(
+ requireNonNull(time, "time"),
+ typeFactory.createSqlType(SqlTypeName.TIME_TZ, precision),
+ SqlTypeName.TIME_TZ);
+ }
+
// CHECKSTYLE: IGNORE 1
/** @deprecated Use {@link #makeTimestampLiteral(TimestampString, int)}. */
@Deprecated // to be removed before 2.0
@@ -1301,6 +1329,15 @@
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
}
+ public RexLiteral makeTimestampTzLiteral(
+ TimestampWithTimeZoneString timestamp,
+ int precision) {
+ return makeLiteral(
+ requireNonNull(timestamp, "timestamp"),
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, precision),
+ SqlTypeName.TIMESTAMP_TZ);
+ }
+
/**
* Creates a literal representing an interval type, for example
* {@code YEAR TO MONTH} or {@code DOW}.
@@ -1544,8 +1581,12 @@
return DateTimeUtils.ZERO_CALENDAR;
case TIME_WITH_LOCAL_TIME_ZONE:
return new TimeString(0, 0, 0);
+ case TIME_TZ:
+ return new TimeWithTimeZoneString(0, 0, 0, "GMT+00");
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return new TimestampString(0, 1, 1, 0, 0, 0);
+ case TIMESTAMP_TZ:
+ return new TimestampWithTimeZoneString(0, 1, 1, 0, 0, 0, "GMT+00");
default:
throw Util.unexpected(type.getSqlTypeName());
}
@@ -1662,12 +1703,17 @@
return makeTimeLiteral((TimeString) value, type.getPrecision());
case TIME_WITH_LOCAL_TIME_ZONE:
return makeTimeWithLocalTimeZoneLiteral((TimeString) value, type.getPrecision());
+ case TIME_TZ:
+ return makeTimeTzLiteral((TimeWithTimeZoneString) value, type.getPrecision());
case DATE:
return makeDateLiteral((DateString) value);
case TIMESTAMP:
return makeTimestampLiteral((TimestampString) value, type.getPrecision());
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return makeTimestampWithLocalTimeZoneLiteral((TimestampString) value, type.getPrecision());
+ case TIMESTAMP_TZ:
+ return makeTimestampTzLiteral(
+ (TimestampWithTimeZoneString) value, type.getPrecision());
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -1827,6 +1873,14 @@
} else {
return TimeString.fromMillisOfDay((Integer) o);
}
+ case TIME_TZ:
+ if (o instanceof TimeWithTimeZoneString) {
+ return o;
+ } else if (o instanceof Calendar) {
+ return TimeWithTimeZoneString.fromCalendarFields((Calendar) o);
+ } else {
+ throw new AssertionError("Value does not contain time zone");
+ }
case TIME_WITH_LOCAL_TIME_ZONE:
if (o instanceof TimeString) {
return o;
@@ -1861,6 +1915,14 @@
} else {
return TimestampString.fromMillisSinceEpoch((Long) o);
}
+ case TIMESTAMP_TZ:
+ if (o instanceof TimestampWithTimeZoneString) {
+ return o;
+ } else if (o instanceof Calendar) {
+ return TimestampWithTimeZoneString.fromCalendarFields((Calendar) o);
+ } else {
+ throw new AssertionError("Value does not contain time zone");
+ }
default:
return o;
}
diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
index 30f0366..1e8cd76 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
@@ -39,7 +39,9 @@
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Sarg;
import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -334,13 +336,15 @@
case DATE:
return value instanceof DateString;
case TIME:
- return value instanceof TimeString;
case TIME_WITH_LOCAL_TIME_ZONE:
return value instanceof TimeString;
+ case TIME_TZ:
+ return value instanceof TimeWithTimeZoneString;
case TIMESTAMP:
- return value instanceof TimestampString;
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return value instanceof TimestampString;
+ case TIMESTAMP_TZ:
+ return value instanceof TimestampWithTimeZoneString;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -696,11 +700,19 @@
assert value instanceof TimeString;
sb.append(value.toString());
break;
+ case TIME_TZ:
+ assert value instanceof TimeWithTimeZoneString;
+ sb.append(value);
+ break;
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
assert value instanceof TimestampString;
sb.append(value.toString());
break;
+ case TIMESTAMP_TZ:
+ assert value instanceof TimestampWithTimeZoneString;
+ sb.append(value);
+ break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -958,10 +970,12 @@
case DECIMAL:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
return getValueAs(Long.class);
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
return getValueAs(Integer.class);
default:
return value;
@@ -1100,6 +1114,11 @@
return clazz.cast(((TimeString) value).getMillisOfDay());
}
break;
+ case TIME_TZ:
+ if (clazz == Integer.class) {
+ return clazz.cast(((TimeWithTimeZoneString) value).getLocalTimeString().getMillisOfDay());
+ }
+ break;
case TIMESTAMP:
if (clazz == Long.class) {
// Milliseconds since 1970-01-01 00:00:00
@@ -1109,6 +1128,16 @@
return clazz.cast(((TimestampString) value).toCalendar());
}
break;
+ case TIMESTAMP_TZ:
+ if (clazz == Long.class) {
+ return clazz.cast(((TimestampWithTimeZoneString) value)
+ .getLocalTimestampString()
+ .getMillisSinceEpoch());
+ } else if (clazz == Calendar.class) {
+ TimestampWithTimeZoneString ts = (TimestampWithTimeZoneString) value;
+ return clazz.cast(ts.getLocalTimestampString().toCalendar(ts.getTimeZone()));
+ }
+ break;
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
if (clazz == Long.class) {
// Milliseconds since 1970-01-01 00:00:00
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java
index 7706c0b..8d97af0 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java
@@ -56,7 +56,7 @@
//~ Methods ----------------------------------------------------------------
/** Converts this literal to a {@link TimestampString}. */
- protected TimestampString getTimestamp() {
+ public TimestampString getTimestamp() {
return (TimestampString) requireNonNull(value, "value");
}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java b/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java
index 547bdfb..9b0434d 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java
@@ -158,9 +158,9 @@
// instead of direct unparsing with enum name.
// i.e. TIME_WITH_LOCAL_TIME_ZONE(3)
// would be unparsed as "time(3) with local time zone".
- final boolean isWithLocalTimeZone = isWithLocalTimeZoneDef(sqlTypeName);
- if (isWithLocalTimeZone) {
- writer.keyword(stripLocalTimeZoneDef(sqlTypeName).name());
+ final boolean isWithTimeZone = isWithTimeZoneDef(sqlTypeName);
+ if (isWithTimeZone) {
+ writer.keyword(stripTimeZoneDef(sqlTypeName).name());
} else {
writer.keyword(getTypeName().getSimple());
}
@@ -176,8 +176,12 @@
writer.endList(frame);
}
- if (isWithLocalTimeZone) {
- writer.keyword("WITH LOCAL TIME ZONE");
+ if (isWithTimeZone) {
+ writer.keyword("WITH");
+ if (isWithLocalTimeZoneDef(sqlTypeName)) {
+ writer.keyword("LOCAL");
+ }
+ writer.keyword("TIME ZONE");
}
if (writer.getDialect().supportsCharSet() && charSetName != null) {
@@ -245,16 +249,23 @@
}
}
+ private static boolean isWithTimeZoneDef(SqlTypeName typeName) {
+ return SqlTypeName.TZ_TYPES.contains(typeName);
+ }
+
/**
- * Remove the local time zone definition of the {@code typeName}.
+ * Remove the local time zone definition
+ * or the time zone definition of the {@code typeName}.
*
* @param typeName Type name
- * @return new type name without local time zone definition
+ * @return new type name without (local) time zone definition
*/
- private static SqlTypeName stripLocalTimeZoneDef(SqlTypeName typeName) {
+ private static SqlTypeName stripTimeZoneDef(SqlTypeName typeName) {
switch (typeName) {
+ case TIME_TZ:
case TIME_WITH_LOCAL_TIME_ZONE:
return SqlTypeName.TIME;
+ case TIMESTAMP_TZ:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return SqlTypeName.TIMESTAMP;
default:
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
index eaf47de..7046fee 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
@@ -37,8 +37,10 @@
SQL_DATE(SqlTypeName.DATE),
SQL_TIME(SqlTypeName.TIME),
SQL_TIME_WITH_LOCAL_TIME_ZONE(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE),
+ SQL_TIME_WITH_TIME_ZONE(SqlTypeName.TIME_TZ),
SQL_TIMESTAMP(SqlTypeName.TIMESTAMP),
SQL_TIMESTAMP_WITH_LOCAL_TIME_ZONE(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE),
+ SQL_TIMESTAMP_WITH_TIME_ZONE(SqlTypeName.TIMESTAMP_TZ),
SQL_DECIMAL(SqlTypeName.DECIMAL),
SQL_NUMERIC(SqlTypeName.DECIMAL),
SQL_BOOLEAN(SqlTypeName.BOOLEAN),
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java
index 67c5d00..ae41cbe 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java
@@ -37,7 +37,9 @@
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -205,10 +207,15 @@
case DATE:
return value instanceof DateString;
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
return value instanceof TimeString;
+ case TIME_TZ:
+ return value instanceof TimeWithTimeZoneString;
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return value instanceof TimestampString;
+ case TIMESTAMP_TZ:
+ return value instanceof TimestampWithTimeZoneString;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -893,6 +900,14 @@
return new SqlTimestampLiteral(ts, precision, typeName, pos);
}
+ /** Creates a TIMESTAMP WITH TIME ZONE literal. */
+ public static SqlTimestampTzLiteral createTimestamp(
+ TimestampWithTimeZoneString ts,
+ int precision,
+ SqlParserPos pos) {
+ return new SqlTimestampTzLiteral(ts, precision, pos);
+ }
+
@Deprecated // to be removed before 2.0
public static SqlTimeLiteral createTime(
Calendar calendar,
@@ -908,6 +923,12 @@
return new SqlTimeLiteral(t, precision, false, pos);
}
+ public static SqlTimeTzLiteral createTime(
+ TimeWithTimeZoneString t,
+ int precision,
+ SqlParserPos pos) {
+ return new SqlTimeTzLiteral(t, precision, pos);
+ }
/**
* Creates an interval literal.
*
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimeTzLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimeTzLiteral.java
new file mode 100644
index 0000000..19dd05f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlTimeTzLiteral.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.TimeWithTimeZoneString;
+
+import com.google.common.base.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A SQL literal representing a TIME WITH TIME ZONE value, for example <code>TIME WITH TIME ZONE
+ * '14:33:44.567 GMT+08'</code>.
+ *
+ * <p>Create values using {@link SqlLiteral#createTime}.
+ */
+public class SqlTimeTzLiteral extends SqlAbstractDateTimeLiteral {
+ //~ Constructors -----------------------------------------------------------
+
+ SqlTimeTzLiteral(TimeWithTimeZoneString t, int precision,
+ SqlParserPos pos) {
+ super(t, true, SqlTypeName.TIME_TZ, precision, pos);
+ Preconditions.checkArgument(this.precision >= 0);
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ /** Converts this literal to a {@link TimeWithTimeZoneString}. */
+ protected TimeWithTimeZoneString getTime() {
+ return (TimeWithTimeZoneString) Objects.requireNonNull(value, "value");
+ }
+
+ @Override public SqlTimeTzLiteral clone(SqlParserPos pos) {
+ return new SqlTimeTzLiteral(getTime(), precision, pos);
+ }
+
+ @Override public String toString() {
+ return "TIME WITH TIME ZONE '" + toFormattedString() + "'";
+ }
+
+ /**
+ * Returns e.g. '03:05:67.456 GMT+00:00'.
+ */
+ @Override public String toFormattedString() {
+ return getTime().toString(precision);
+ }
+
+ @Override public void unparse(
+ SqlWriter writer,
+ int leftPrec,
+ int rightPrec) {
+ writer.getDialect().unparseDateTimeLiteral(writer, this, leftPrec, rightPrec);
+ }
+}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java
index d70b677..8d1e6ec 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java
@@ -26,7 +26,7 @@
/**
* A SQL literal representing a TIMESTAMP value, for example <code>TIMESTAMP
- * '1969-07-21 03:15 GMT'</code>.
+ * '1969-07-21 03:15'</code>.
*
* <p>Create values using {@link SqlLiteral#createTimestamp}.
*/
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimestampTzLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimestampTzLiteral.java
new file mode 100644
index 0000000..0e79038
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlTimestampTzLiteral.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
+
+import com.google.common.base.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A SQL literal representing a TIMESTAMP WITH TIME ZONE value, for example <code>TIMESTAMP
+ * '1969-07-21 03:15 GMT+00:00'</code>.
+ *
+ * <p>Create values using {@link SqlLiteral#createTimestamp}.
+ */
+public class SqlTimestampTzLiteral extends SqlAbstractDateTimeLiteral {
+ //~ Constructors -----------------------------------------------------------
+
+ SqlTimestampTzLiteral(TimestampWithTimeZoneString ts, int precision, SqlParserPos pos) {
+ super(ts, false, SqlTypeName.TIMESTAMP_TZ, precision, pos);
+ Preconditions.checkArgument(this.precision >= 0);
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ @Override public SqlTimestampTzLiteral clone(SqlParserPos pos) {
+ return new SqlTimestampTzLiteral(
+ getTimestampTz(), precision, pos);
+ }
+
+ @Override public String toString() {
+ return getTypeName() + " '" + toFormattedString() + "'";
+ }
+
+ @Override public String toFormattedString() {
+ TimestampWithTimeZoneString ts = getTimestampTz();
+ if (precision > 0) {
+ ts = ts.round(precision);
+ }
+ return ts.toString(precision);
+ }
+
+ TimestampWithTimeZoneString getTimestampTz() {
+ return (TimestampWithTimeZoneString) Objects.requireNonNull(value, "value");
+ }
+
+ @Override public void unparse(
+ SqlWriter writer,
+ int leftPrec,
+ int rightPrec) {
+ writer.getDialect().unparseDateTimeLiteral(writer, this, leftPrec, rightPrec);
+ }
+}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUnknownLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlUnknownLiteral.java
index a6cbbbb..a0a811e 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUnknownLiteral.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUnknownLiteral.java
@@ -57,10 +57,14 @@
return SqlParserUtil.parseDateLiteral(getValue(), pos);
case TIME:
return SqlParserUtil.parseTimeLiteral(getValue(), pos);
+ case TIME_TZ:
+ return SqlParserUtil.parseTimeTzLiteral(getValue(), pos);
case TIMESTAMP:
return SqlParserUtil.parseTimestampLiteral(getValue(), pos);
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return SqlParserUtil.parseTimestampWithLocalTimeZoneLiteral(getValue(), pos);
+ case TIMESTAMP_TZ:
+ return SqlParserUtil.parseTimestampTzLiteral(getValue(), pos);
default:
throw Util.unexpected(typeName);
}
diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
index ca3c0a7..44fecaa 100644
--- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
@@ -36,7 +36,9 @@
import org.apache.calcite.sql.SqlPrefixOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
import org.apache.calcite.sql.SqlTimeLiteral;
+import org.apache.calcite.sql.SqlTimeTzLiteral;
import org.apache.calcite.sql.SqlTimestampLiteral;
+import org.apache.calcite.sql.SqlTimestampTzLiteral;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.impl.SqlParserImpl;
@@ -44,7 +46,9 @@
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.PrecedenceClimbingParser;
import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
@@ -65,6 +69,7 @@
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
+import java.util.TimeZone;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -352,6 +357,31 @@
return SqlLiteral.createTime(t, pt.getPrecision(), pos);
}
+ public static SqlTimeTzLiteral parseTimeTzLiteral(
+ String s, SqlParserPos pos) {
+ // We expect the string to end in a timezone.
+ final int lastSpace = s.lastIndexOf(" ");
+ DateTimeUtils.PrecisionTime pt = null;
+ if (lastSpace >= 0) {
+ final String timeZone = s.substring(lastSpace + 1);
+ final String time = s.substring(0, lastSpace);
+
+ final TimeZone tz = TimeZone.getTimeZone(timeZone);
+ if (tz != null) {
+ pt =
+ DateTimeUtils.parsePrecisionDateTimeLiteral(time, Format.get().time, tz, -1);
+ }
+ }
+ if (pt == null) {
+ throw SqlUtil.newContextException(pos,
+ RESOURCE.illegalLiteral("TIME WITH TIME ZONE", s,
+ RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str()));
+ }
+ final TimeWithTimeZoneString t = TimeWithTimeZoneString.fromCalendarFields(pt.getCalendar())
+ .withFraction(pt.getFraction());
+ return SqlLiteral.createTime(t, pt.getPrecision(), pos);
+ }
+
public static SqlTimestampLiteral parseTimestampLiteral(String s,
SqlParserPos pos) {
return parseTimestampLiteral(SqlTypeName.TIMESTAMP, s, pos);
@@ -363,6 +393,25 @@
pos);
}
+ public static SqlTimestampTzLiteral parseTimestampTzLiteral(
+ String s, SqlParserPos pos) {
+ // We expect the string to end in a timezone.
+ int lastSpace = s.lastIndexOf(" ");
+ if (lastSpace >= 0) {
+ final String timeZone = s.substring(lastSpace + 1);
+ final String timestamp = s.substring(0, lastSpace);
+ TimeZone tz = TimeZone.getTimeZone(timeZone);
+ if (tz != null) {
+ SqlTimestampLiteral ts = parseTimestampLiteral(SqlTypeName.TIMESTAMP, timestamp, pos);
+ TimestampWithTimeZoneString tsz = new TimestampWithTimeZoneString(ts.getTimestamp(), tz);
+ return SqlLiteral.createTimestamp(tsz, ts.getPrec(), pos);
+ }
+ }
+ throw SqlUtil.newContextException(pos,
+ RESOURCE.illegalLiteral("TIMESTAMP WITH TIME ZONE", s,
+ RESOURCE.badFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING).str()));
+ }
+
private static SqlTimestampLiteral parseTimestampLiteral(SqlTypeName typeName,
String s, SqlParserPos pos) {
final Format format = Format.get();
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index 342284d..386ed96 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -377,6 +377,13 @@
explicit(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
/**
+ * Type-inference strategy whereby the result type of a call is TIMESTAMP
+ * WITH TIME ZONE.
+ */
+ public static final SqlReturnTypeInference TIMESTAMP_TZ =
+ explicit(SqlTypeName.TIMESTAMP_TZ);
+
+ /**
* Type-inference strategy whereby the result type of a call is nullable
* TIMESTAMP WITH LOCAL TIME ZONE.
*/
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
index 6167c4d..c77f0b1 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
@@ -175,6 +175,9 @@
rules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
EnumSet.of(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE));
+ // TIME WITH TIME ZONE is assignable from...
+ rules.add(SqlTypeName.TIME_TZ, EnumSet.of(SqlTypeName.TIME_TZ));
+
// TIMESTAMP is assignable from ...
rules.add(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP));
@@ -182,6 +185,9 @@
rules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
EnumSet.of(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE));
+ // TIMESTAMP WITH TIME ZONE is assignable from...
+ rules.add(SqlTypeName.TIMESTAMP_TZ, EnumSet.of(SqlTypeName.TIMESTAMP_TZ));
+
// GEOMETRY is assignable from ...
rule.clear();
rule.add(SqlTypeName.GEOMETRY);
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java
index 7370c1d..4ab42df 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java
@@ -120,6 +120,7 @@
rule.add(SqlTypeName.VARCHAR);
rule.add(SqlTypeName.TIMESTAMP);
rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.TIMESTAMP_TZ);
coerceRules.add(SqlTypeName.TINYINT, rule);
coerceRules.add(SqlTypeName.SMALLINT, rule);
@@ -176,8 +177,11 @@
.add(SqlTypeName.BOOLEAN)
.add(SqlTypeName.DATE)
.add(SqlTypeName.TIME)
+ .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.addAll(SqlTypeName.BINARY_TYPES)
.addAll(SqlTypeName.NUMERIC_TYPES)
.addAll(SqlTypeName.INTERVAL_TYPES)
@@ -191,8 +195,11 @@
.add(SqlTypeName.BOOLEAN)
.add(SqlTypeName.DATE)
.add(SqlTypeName.TIME)
+ .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.addAll(SqlTypeName.BINARY_TYPES)
.addAll(SqlTypeName.NUMERIC_TYPES)
.addAll(SqlTypeName.INTERVAL_TYPES)
@@ -214,6 +221,7 @@
coerceRules.copyValues(SqlTypeName.DATE)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.add(SqlTypeName.CHAR)
.add(SqlTypeName.VARCHAR)
.addAll(SqlTypeName.BINARY_TYPES)
@@ -223,8 +231,10 @@
coerceRules.add(SqlTypeName.TIME,
coerceRules.copyValues(SqlTypeName.TIME)
.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.add(SqlTypeName.CHAR)
.add(SqlTypeName.VARCHAR)
.addAll(SqlTypeName.BINARY_TYPES)
@@ -233,9 +243,24 @@
// TIME WITH LOCAL TIME ZONE is castable from...
coerceRules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
coerceRules.copyValues(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.TIME)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
+ .add(SqlTypeName.CHAR)
+ .add(SqlTypeName.VARCHAR)
+ .addAll(SqlTypeName.BINARY_TYPES)
+ .build());
+
+ // TIME WITH TIME ZONE is castable from...
+ coerceRules.add(SqlTypeName.TIME_TZ,
+ coerceRules.copyValues(SqlTypeName.TIME_TZ)
+ .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.add(SqlTypeName.CHAR)
.add(SqlTypeName.VARCHAR)
.addAll(SqlTypeName.BINARY_TYPES)
@@ -245,9 +270,11 @@
coerceRules.add(SqlTypeName.TIMESTAMP,
coerceRules.copyValues(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.add(SqlTypeName.DATE)
.add(SqlTypeName.TIME)
.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.CHAR)
.add(SqlTypeName.VARCHAR)
.addAll(SqlTypeName.BINARY_TYPES)
@@ -257,10 +284,27 @@
// TIMESTAMP WITH LOCAL TIME ZONE is castable from...
coerceRules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
coerceRules.copyValues(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP_TZ)
.add(SqlTypeName.TIMESTAMP)
.add(SqlTypeName.DATE)
.add(SqlTypeName.TIME)
.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
+ .add(SqlTypeName.CHAR)
+ .add(SqlTypeName.VARCHAR)
+ .addAll(SqlTypeName.BINARY_TYPES)
+ .addAll(SqlTypeName.NUMERIC_TYPES)
+ .build());
+
+ // TIMESTAMP WITH TIME ZONE is castable from...
+ coerceRules.add(SqlTypeName.TIMESTAMP_TZ,
+ coerceRules.copyValues(SqlTypeName.TIMESTAMP_TZ)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIME)
+ .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME_TZ)
.add(SqlTypeName.CHAR)
.add(SqlTypeName.VARCHAR)
.addAll(SqlTypeName.BINARY_TYPES)
@@ -292,6 +336,7 @@
rule.add(SqlTypeName.BOOLEAN);
rule.add(SqlTypeName.TIMESTAMP);
rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.TIMESTAMP_TZ);
coerceRules.add(SqlTypeName.TINYINT, rule);
coerceRules.add(SqlTypeName.SMALLINT, rule);
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
index 58b6167..0509022 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
@@ -179,9 +179,13 @@
case DATE:
return ImmutableList.of(SqlTypeName.DATE);
case TIME:
- return ImmutableList.of(SqlTypeName.TIME, SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ return ImmutableList.of(SqlTypeName.TIME,
+ SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
+ SqlTypeName.TIME_TZ);
case TIMESTAMP:
- return ImmutableList.of(SqlTypeName.TIMESTAMP, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ return ImmutableList.of(SqlTypeName.TIMESTAMP,
+ SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+ SqlTypeName.TIMESTAMP_TZ);
case BOOLEAN:
return SqlTypeName.BOOLEAN_TYPES;
case INTERVAL_YEAR_MONTH:
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
index 442e5ce..0c856e3 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
@@ -65,12 +65,16 @@
DATE(PrecScale.NO_NO, false, Types.DATE, SqlTypeFamily.DATE),
TIME(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME,
SqlTypeFamily.TIME),
- TIME_WITH_LOCAL_TIME_ZONE(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.OTHER,
+ TIME_WITH_LOCAL_TIME_ZONE(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME,
+ SqlTypeFamily.TIME),
+ TIME_TZ(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME,
SqlTypeFamily.TIME),
TIMESTAMP(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIMESTAMP,
SqlTypeFamily.TIMESTAMP),
TIMESTAMP_WITH_LOCAL_TIME_ZONE(PrecScale.NO_NO | PrecScale.YES_NO, false,
Types.TIMESTAMP, SqlTypeFamily.TIMESTAMP),
+ TIMESTAMP_TZ(PrecScale.NO_NO | PrecScale.YES_NO, false,
+ Types.TIMESTAMP, SqlTypeFamily.TIMESTAMP),
INTERVAL_YEAR(PrecScale.NO_NO, false, Types.OTHER,
SqlTypeFamily.INTERVAL_YEAR_MONTH),
INTERVAL_YEAR_MONTH(PrecScale.NO_NO, false, Types.OTHER,
@@ -159,7 +163,8 @@
INTERVAL_DAY, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
INTERVAL_DAY_SECOND, INTERVAL_HOUR, INTERVAL_HOUR_MINUTE,
INTERVAL_HOUR_SECOND, INTERVAL_MINUTE, INTERVAL_MINUTE_SECOND,
- INTERVAL_SECOND, TIME_WITH_LOCAL_TIME_ZONE, TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+ INTERVAL_SECOND, TIME_WITH_LOCAL_TIME_ZONE, TIME_TZ,
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIMESTAMP_TZ,
FLOAT, MULTISET, DISTINCT, STRUCTURED, ROW, CURSOR, COLUMN_LIST);
public static final List<SqlTypeName> BOOLEAN_TYPES =
@@ -193,8 +198,13 @@
ImmutableList.of(GEOMETRY);
public static final List<SqlTypeName> DATETIME_TYPES =
- ImmutableList.of(DATE, TIME, TIME_WITH_LOCAL_TIME_ZONE,
- TIMESTAMP, TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ ImmutableList.of(DATE, TIME, TIME_WITH_LOCAL_TIME_ZONE, TIME_TZ,
+ TIMESTAMP, TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIMESTAMP_TZ);
+
+ /** Types that contain time zone information. */
+ public static final List<SqlTypeName> TZ_TYPES =
+ ImmutableList.of(TIME_WITH_LOCAL_TIME_ZONE, TIME_TZ,
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIMESTAMP_TZ);
public static final Set<SqlTypeName> YEAR_INTERVAL_TYPES =
Sets.immutableEnumSet(SqlTypeName.INTERVAL_YEAR,
@@ -309,7 +319,12 @@
* matches the given name, or throws {@link IllegalArgumentException}; never
* returns null. */
public static SqlTypeName lookup(String tag) {
- String tag2 = tag.replace(' ', '_');
+ // Special handling for TIME WITH TIME ZONE and
+ // TIMESTAMP WITH TIME ZONE, whose type names are TIME_TZ and TIMESTAMP_TZ.
+ // We know that the type name is always uppercase, because it is
+ // inserted in the tag by the parser.
+ final String tag1 = tag.replace("WITH TIME ZONE", "TZ");
+ final String tag2 = tag1.replace(' ', '_');
return valueOf(tag2);
}
@@ -776,8 +791,10 @@
case BINARY:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIME_TZ:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP_TZ:
return 1;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java
index 313fda1..2b99128 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java
@@ -32,6 +32,7 @@
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import java.math.BigDecimal;
@@ -124,6 +125,10 @@
return rexBuilder.makeTimestampWithLocalTimeZoneLiteral(
literal.getValueAs(TimestampString.class),
((SqlTimestampLiteral) literal).getPrec());
+ case TIMESTAMP_TZ:
+ return rexBuilder.makeTimestampTzLiteral(
+ literal.getValueAs(TimestampWithTimeZoneString.class),
+ ((SqlTimestampLiteral) literal).getPrec());
case TIME:
return rexBuilder.makeTimeLiteral(
literal.getValueAs(TimeString.class),
diff --git a/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
index a57e949..2087b79 100644
--- a/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
+++ b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
@@ -74,6 +74,12 @@
return withFraction(DateTimeStringUtils.pad(3, millis));
}
+ /** Creates a TimeWithTimeZoneString from a Calendar. */
+ public static TimeWithTimeZoneString fromCalendarFields(Calendar calendar) {
+ TimeString ts = TimeString.fromCalendarFields(calendar);
+ return new TimeWithTimeZoneString(ts, calendar.getTimeZone());
+ }
+
/** Sets the fraction field of a {@code TimeString} to a given number
* of nanoseconds. Nukes the value set via {@link #withMillis(int)}.
*
diff --git a/core/src/main/java/org/apache/calcite/util/TimestampString.java b/core/src/main/java/org/apache/calcite/util/TimestampString.java
index 45d7eb9..c6c7c18 100644
--- a/core/src/main/java/org/apache/calcite/util/TimestampString.java
+++ b/core/src/main/java/org/apache/calcite/util/TimestampString.java
@@ -25,6 +25,7 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Calendar;
+import java.util.TimeZone;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkArgument;
@@ -222,6 +223,10 @@
return Util.calendar(getMillisSinceEpoch());
}
+ public Calendar toCalendar(TimeZone timeZone) {
+ return Util.calendar(getMillisSinceEpoch(), timeZone);
+ }
+
/** Converts this TimestampString to a string, truncated or padded with
* zeros to a given precision. */
public String toString(int precision) {
diff --git a/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
index 8e31656..e3c08bb 100644
--- a/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
+++ b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
@@ -100,6 +100,12 @@
localDateTime.withFraction(fraction), timeZone);
}
+ /** Creates a TimestampWithTimeZoneString from a Calendar. */
+ public static TimestampWithTimeZoneString fromCalendarFields(Calendar calendar) {
+ TimestampString ts = TimestampString.fromCalendarFields(calendar);
+ return new TimestampWithTimeZoneString(ts, calendar.getTimeZone());
+ }
+
public TimestampWithTimeZoneString withTimeZone(TimeZone timeZone) {
if (this.timeZone.equals(timeZone)) {
return this;
@@ -193,4 +199,7 @@
return localDateTime;
}
+ public TimeZone getTimeZone() {
+ return timeZone;
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java
index 2beda0a..0674009 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -2582,8 +2582,7 @@
}
}
- /** Creates a {@link Calendar} in the UTC time zone and root locale.
- * Does not use the time zone or locale. */
+ /** Creates a {@link Calendar} in the UTC time zone and root locale. */
public static Calendar calendar() {
return Calendar.getInstance(DateTimeUtils.UTC_ZONE, Locale.ROOT);
}
@@ -2596,6 +2595,13 @@
return calendar;
}
+ /** Creates a {@link Calendar} in the specified time zone. */
+ public static Calendar calendar(long millis, TimeZone timeZone) {
+ Calendar calendar = Calendar.getInstance(timeZone, Locale.ROOT);
+ calendar.setTimeInMillis(millis);
+ return calendar;
+ }
+
/**
* Returns a {@code Collector} that accumulates the input elements into a
* Guava {@link ImmutableList} via a {@link ImmutableList.Builder}.
diff --git a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
index e0dce8b..45c8935 100644
--- a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
+++ b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
@@ -319,7 +319,7 @@
CalciteAssert.hr()
.with(CalciteRemoteDriverTest::getRemoteConnection)
.metaData(CalciteRemoteDriverTest::getTypeInfo)
- .returns(CalciteAssert.checkResultCount(is(41)));
+ .returns(CalciteAssert.checkResultCount(is(43)));
}
@Test void testRemoteTableTypes() {
diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
index bcba216..9db5f8e 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
@@ -39,6 +39,7 @@
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
@@ -438,6 +439,77 @@
assertThat(literal.getValue3() instanceof Long, is(true));
}
+ /** Tests
+ * {@link RexBuilder#makeTimestampTzLiteral(TimestampWithTimeZoneString, int)}. */
+ @Test void testTimestampTzLiterals() {
+ final RelDataTypeFactory typeFactory =
+ new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ final RelDataType timestampType =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ);
+ final RelDataType timestampType3 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 3);
+ final RelDataType timestampType9 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 9);
+ final RelDataType timestampType18 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 18);
+ final RexBuilder builder = new RexBuilder(typeFactory);
+
+ // The new way
+ final TimestampWithTimeZoneString ts =
+ new TimestampWithTimeZoneString(1969, 7, 21, 2, 56, 15,
+ TimeZone.getTimeZone("PST").getID());
+ checkTimestampTz(builder.makeLiteral(ts, timestampType));
+
+ // Now with milliseconds
+ final TimestampWithTimeZoneString ts2 = ts.withMillis(56);
+ assertThat(ts2, hasToString("1969-07-21 02:56:15.056 PST"));
+ final RexLiteral literal2 =
+ builder.makeLiteral(ts2, timestampType3);
+ assertThat(literal2.getValue(), hasToString("1969-07-21 02:56:15.056 PST"));
+
+ // Now with nanoseconds
+ final TimestampWithTimeZoneString ts3 = ts.withNanos(56);
+ final RexLiteral literal3 =
+ builder.makeLiteral(ts3, timestampType9);
+ assertThat(literal3.getValueAs(TimestampWithTimeZoneString.class),
+ hasToString("1969-07-21 02:56:15 PST"));
+ final TimestampWithTimeZoneString ts3b = ts.withNanos(2345678);
+ final RexLiteral literal3b =
+ builder.makeLiteral(ts3b, timestampType9);
+ assertThat(literal3b.getValueAs(TimestampWithTimeZoneString.class),
+ hasToString("1969-07-21 02:56:15.002 PST"));
+
+ // Now with a very long fraction
+ final TimestampWithTimeZoneString ts4 = ts.withFraction("102030405060708090102");
+ final RexLiteral literal4 =
+ builder.makeLiteral(ts4, timestampType18);
+ assertThat(literal4.getValueAs(TimestampWithTimeZoneString.class),
+ hasToString("1969-07-21 02:56:15.102 PST"));
+
+ // toString
+ assertThat(ts2.round(1), hasToString("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(2), hasToString("1969-07-21 02:56:15.05 PST"));
+ assertThat(ts2.round(3), hasToString("1969-07-21 02:56:15.056 PST"));
+ assertThat(ts2.round(4), hasToString("1969-07-21 02:56:15.056 PST"));
+
+ assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000 PST"));
+ assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0 PST"));
+ assertThat(ts2.toString(0), is("1969-07-21 02:56:15 PST"));
+
+ assertThat(ts2.round(0), hasToString("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0 PST"));
+ assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00 PST"));
+ }
+
+ private void checkTimestampTz(RexLiteral literal) {
+ assertThat(literal,
+ hasToString("1969-07-21 02:56:15 PST:TIMESTAMP_TZ(0)"));
+ assertThat(literal.getValue() instanceof TimestampWithTimeZoneString, is(true));
+ assertThat(literal.getValue2() instanceof Long, is(true));
+ assertThat(literal.getValue3() instanceof Long, is(true));
+ }
+
/** Tests {@link RexBuilder#makeTimeLiteral(TimeString, int)}. */
@Test void testTimeLiteral() {
final RelDataTypeFactory typeFactory =
@@ -827,7 +899,11 @@
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE),
relDataType -> new TimeString(0, 0, 0)),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE),
- relDataType -> new TimestampString(0, 1, 1, 0, 0, 0)));
+ relDataType -> new TimestampString(0, 1, 1, 0, 0, 0)),
+ type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIME_TZ),
+ relDataType -> new TimeWithTimeZoneString(0, 0, 0, "GMT+00:00")),
+ type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ),
+ relDataType -> new TimestampWithTimeZoneString(0, 1, 1, 0, 0, 0, "GMT+00:00")));
}
/** Test case for
diff --git a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
index 5a0d965..a85fff1 100644
--- a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
@@ -284,7 +284,9 @@
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME_TZ);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP_TZ);
}
private void checkCreateSqlTypeWithPrecision(
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 1308aa9..21e2e91 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -278,8 +278,11 @@
.fails("(?s).*Illegal TIME literal.*");
expr("^TIMESTAMP '12-21-99, 12:30:00'^")
.fails("(?s).*Illegal TIMESTAMP literal.*");
+
expr("^TIMESTAMP WITH LOCAL TIME ZONE '12-21-99, 12:30:00'^")
.fails("(?s).*Illegal TIMESTAMP WITH LOCAL TIME ZONE literal.*");
+ expr("^TIMESTAMP WITH TIME ZONE '12-21-99, 12:30:00'^")
+ .fails("(?s).*Illegal TIMESTAMP literal.*");
}
/** PostgreSQL and Redshift allow TIMESTAMP literals that contain only a
@@ -1310,12 +1313,16 @@
.columnType("TIME(0) NOT NULL");
expr("cast('abc' as time with local time zone)")
.columnType("TIME_WITH_LOCAL_TIME_ZONE(0) NOT NULL");
+ expr("cast('abc' as time with time zone)")
+ .columnType("TIME_TZ(0) NOT NULL");
expr("cast('abc' as time(3))")
.columnType("TIME(3) NOT NULL");
expr("cast('abc' as time(3) without time zone)")
.columnType("TIME(3) NOT NULL");
expr("cast('abc' as time(3) with local time zone)")
.columnType("TIME_WITH_LOCAL_TIME_ZONE(3) NOT NULL");
+ expr("cast('abc' as time(3) with time zone)")
+ .columnType("TIME_TZ(3) NOT NULL");
// test cast to timestamp type.
expr("cast('abc' as timestamp)")
.columnType("TIMESTAMP(0) NOT NULL");
@@ -1323,12 +1330,16 @@
.columnType("TIMESTAMP(0) NOT NULL");
expr("cast('abc' as timestamp with local time zone)")
.columnType("TIMESTAMP_WITH_LOCAL_TIME_ZONE(0) NOT NULL");
+ expr("cast('abc' as timestamp with time zone)")
+ .columnType("TIMESTAMP_TZ(0) NOT NULL");
expr("cast('abc' as timestamp(3))")
.columnType("TIMESTAMP(3) NOT NULL");
expr("cast('abc' as timestamp(3) without time zone)")
.columnType("TIMESTAMP(3) NOT NULL");
expr("cast('abc' as timestamp(3) with local time zone)")
.columnType("TIMESTAMP_WITH_LOCAL_TIME_ZONE(3) NOT NULL");
+ expr("cast('abc' as timestamp(3) with time zone)")
+ .columnType("TIMESTAMP_TZ(3) NOT NULL");
}
@Test void testCastRegisteredType() {
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 21edb54..d8712e5 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -1167,9 +1167,11 @@
| VARBINARY(n), BINARY VARYING(n) | Variable-length binary string | As BINARY(n)
| DATE | Date | Example: DATE '1969-07-20'
| TIME | Time of day | Example: TIME '20:17:40'
+| TIME WITH LOCAL TIME ZONE | Time of day with local time zone | Example: TIME WITH LOCAL TIME ZONE '20:17:40'
+| TIME WITH TIME ZONE | Time of day with time zone | Example: TIME '20:17:40 GMT+08'
| TIMESTAMP [ WITHOUT TIME ZONE ] | Date and time | Example: TIMESTAMP '1969-07-20 20:17:40'
-| TIMESTAMP WITH LOCAL TIME ZONE | Date and time with local time zone | Example: TIMESTAMP '1969-07-20 20:17:40 America/Los Angeles'
-| TIMESTAMP WITH TIME ZONE | Date and time with time zone | Example: TIMESTAMP '1969-07-20 20:17:40 America/Los Angeles'
+| TIMESTAMP WITH LOCAL TIME ZONE | Date and time with local time zone | Example: TIMESTAMP WITH LOCAL TIME ZONE '1969-07-20 20:17:40'
+| TIMESTAMP WITH TIME ZONE | Date and time with time zone | Example: TIMESTAMP WITH TIME ZONE '1969-07-20 20:17:40 America/Los Angeles'
| INTERVAL timeUnit [ TO timeUnit ] | Date time interval | Examples: INTERVAL '1-5' YEAR TO MONTH, INTERVAL '45' DAY, INTERVAL '1 2:34:56.789' DAY TO SECOND
| GEOMETRY | Geometry | Examples: ST_GeomFromText('POINT (30 10)')
diff --git a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
index a8ed3e6..c5bce8b 100644
--- a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -1783,7 +1783,7 @@
.ok("CEIL((`X` + INTERVAL '1:20' MINUTE TO SECOND) TO MILLENNIUM)");
}
- @Test void testCast() {
+ @Test public void testCast() {
expr("cast(x as boolean)")
.ok("CAST(`X` AS BOOLEAN)");
expr("cast(x as integer)")
@@ -1798,10 +1798,14 @@
.ok("CAST(`X` AS TIME)");
expr("cast(x as time with local time zone)")
.ok("CAST(`X` AS TIME WITH LOCAL TIME ZONE)");
+ expr("cast(x as time with time zone)")
+ .ok("CAST(`X` AS TIME WITH TIME ZONE)");
expr("cast(x as timestamp without time zone)")
.ok("CAST(`X` AS TIMESTAMP)");
expr("cast(x as timestamp with local time zone)")
.ok("CAST(`X` AS TIMESTAMP WITH LOCAL TIME ZONE)");
+ expr("cast(x as timestamp with time zone)")
+ .ok("CAST(`X` AS TIMESTAMP WITH TIME ZONE)");
expr("cast(x as time(0))")
.ok("CAST(`X` AS TIME(0))");
expr("cast(x as time(0) without time zone)")
@@ -1846,14 +1850,6 @@
}
@Test void testCastFails() {
- expr("cast(x as time with ^time^ zone)")
- .fails("(?s).*Encountered \"time\" at .*");
- expr("cast(x as time(0) with ^time^ zone)")
- .fails("(?s).*Encountered \"time\" at .*");
- expr("cast(x as timestamp with ^time^ zone)")
- .fails("(?s).*Encountered \"time\" at .*");
- expr("cast(x as timestamp(0) with ^time^ zone)")
- .fails("(?s).*Encountered \"time\" at .*");
expr("cast(x as varchar(10) ^with^ local time zone)")
.fails("(?s).*Encountered \"with\" at line 1, column 23.\n.*");
expr("cast(x as varchar(10) ^without^ time zone)")
@@ -5550,8 +5546,11 @@
expr("^DATE '12/21/99'^").same();
expr("^TIME '1230:33'^").same();
expr("^TIME '12:00:00 PM'^").same();
+ expr("^TIME WITH LOCAL TIME ZONE '12:00:00 PM'^").same();
+ expr("^TIME WITH TIME ZONE '12:00:00 PM GMT+0:00'^").same();
expr("TIMESTAMP '12-21-99, 12:30:00'").same();
expr("TIMESTAMP WITH LOCAL TIME ZONE '12-21-99, 12:30:00'").same();
+ expr("TIMESTAMP WITH TIME ZONE '12-21-99, 12:30:00 GMT+0:00'").same();
expr("DATETIME '12-21-99, 12:30:00'").same();
}