http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/DateString.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/DateString.java b/core/src/main/java/org/apache/calcite/util/DateString.java new file mode 100644 index 0000000..cea9df3 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/util/DateString.java @@ -0,0 +1,98 @@ +/* + * 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.util; + +import org.apache.calcite.avatica.util.DateTimeUtils; + +import com.google.common.base.Preconditions; + +import java.util.Calendar; +import java.util.regex.Pattern; + +/** + * Date literal. + * + * <p>Immutable, internally represented as a string (in ISO format). + */ +public class DateString implements Comparable<DateString> { + private static final Pattern PATTERN = + Pattern.compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"); + + final String v; + + /** Creates a DateString. */ + public DateString(String v) { + this.v = v; + Preconditions.checkArgument(PATTERN.matcher(v).matches(), v); + } + + /** Creates a DateString for year, month, day values. */ + public DateString(int year, int month, int day) { + this(TimestampString.ymd(new StringBuilder(), year, month, day).toString()); + } + + @Override public String toString() { + return v; + } + + @Override public boolean equals(Object o) { + // The value is in canonical form. + return o == this + || o instanceof DateString + && ((DateString) o).v.equals(v); + } + + @Override public int hashCode() { + return v.hashCode(); + } + + @Override public int compareTo(DateString o) { + return v.compareTo(o.v); + } + + /** Creates a DateString from a Calendar. */ + public static DateString fromCalendarFields(Calendar calendar) { + return new DateString(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH)); + } + + /** Returns the number of days since the epoch. */ + public int getDaysSinceEpoch() { + int year = Integer.valueOf(v.substring(0, 4)); + int month = Integer.valueOf(v.substring(5, 7)); + int day = Integer.valueOf(v.substring(8, 10)); + return DateTimeUtils.ymdToUnixDate(year, month, day); + } + + /** Creates a DateString that is a given number of days since the epoch. */ + public static DateString fromDaysSinceEpoch(int days) { + return new DateString(DateTimeUtils.unixDateToString(days)); + } + + /** Returns the number of milliseconds since the epoch. Always a multiple of + * 86,400,000 (the number of milliseconds in a day). */ + public long getMillisSinceEpoch() { + return getDaysSinceEpoch() * DateTimeUtils.MILLIS_PER_DAY; + } + + public Calendar toCalendar() { + return Util.calendar(getMillisSinceEpoch()); + } +} + +// End DateString.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/TimeString.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/TimeString.java b/core/src/main/java/org/apache/calcite/util/TimeString.java new file mode 100644 index 0000000..75aa96b --- /dev/null +++ b/core/src/main/java/org/apache/calcite/util/TimeString.java @@ -0,0 +1,192 @@ +/* + * 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.util; + +import org.apache.calcite.avatica.util.DateTimeUtils; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import java.util.Calendar; +import java.util.regex.Pattern; + +/** + * Time literal. + * + * <p>Immutable, internally represented as a string (in ISO format), + * and can support unlimited precision (milliseconds, nanoseconds). + */ +public class TimeString implements Comparable<TimeString> { + private static final Pattern PATTERN = + Pattern.compile("[0-9][0-9]:[0-9][0-9]:[0-9][0-9](\\.[0-9]*[1-9])?"); + + final String v; + + /** Creates a TimeString. */ + public TimeString(String v) { + this.v = v; + Preconditions.checkArgument(PATTERN.matcher(v).matches(), v); + } + + /** Creates a TimeString for hour, minute, second and millisecond values. */ + public TimeString(int h, int m, int s) { + this(TimestampString.hms(new StringBuilder(), h, m, s).toString()); + } + + /** Sets the fraction field of a {@code TimeString} to a given number + * of milliseconds. Nukes the value set via {@link #withNanos}. + * + * <p>For example, + * {@code new TimeString(1970, 1, 1, 2, 3, 4).withMillis(56)} + * yields {@code TIME '1970-01-01 02:03:04.056'}. */ + public TimeString withMillis(int millis) { + Preconditions.checkArgument(millis >= 0 && millis < 1000); + return withFraction(TimestampString.pad(3, millis)); + } + + /** Sets the fraction field of a {@code TimeString} to a given number + * of nanoseconds. Nukes the value set via {@link #withMillis(int)}. + * + * <p>For example, + * {@code new TimeString(1970, 1, 1, 2, 3, 4).withNanos(56789)} + * yields {@code TIME '1970-01-01 02:03:04.000056789'}. */ + public TimeString withNanos(int nanos) { + Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000); + return withFraction(TimestampString.pad(9, nanos)); + } + + /** Sets the fraction field of a {@code TimeString}. + * The precision is determined by the number of leading zeros. + * Trailing zeros are stripped. + * + * <p>For example, + * {@code new TimeString(1970, 1, 1, 2, 3, 4).withFraction("00506000")} + * yields {@code TIME '1970-01-01 02:03:04.00506'}. */ + public TimeString withFraction(String fraction) { + String v = this.v; + int i = v.indexOf('.'); + if (i >= 0) { + v = v.substring(0, i); + } + while (fraction.endsWith("0")) { + fraction = fraction.substring(0, fraction.length() - 1); + } + if (fraction.length() > 0) { + v = v + "." + fraction; + } + return new TimeString(v); + } + + @Override public String toString() { + return v; + } + + @Override public boolean equals(Object o) { + // The value is in canonical form (no trailing zeros). + return o == this + || o instanceof TimeString + && ((TimeString) o).v.equals(v); + } + + @Override public int hashCode() { + return v.hashCode(); + } + + @Override public int compareTo(TimeString o) { + return v.compareTo(o.v); + } + + /** Creates a TimeString from a Calendar. */ + public static TimeString fromCalendarFields(Calendar calendar) { + return new TimeString( + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + calendar.get(Calendar.SECOND)) + .withMillis(calendar.get(Calendar.MILLISECOND)); + } + + public static TimeString fromMillisOfDay(int i) { + return new TimeString(DateTimeUtils.unixTimeToString(i)) + .withMillis((int) DateTimeUtils.floorMod(i, 1000)); + } + + public TimeString round(int precision) { + Preconditions.checkArgument(precision >= 0); + int targetLength = 9 + precision; + if (v.length() <= targetLength) { + return this; + } + String v = this.v.substring(0, targetLength); + while (v.length() >= 9 && (v.endsWith("0") || v.endsWith("."))) { + v = v.substring(0, v.length() - 1); + } + return new TimeString(v); + } + + public int getMillisOfDay() { + int h = Integer.valueOf(v.substring(0, 2)); + int m = Integer.valueOf(v.substring(3, 5)); + int s = Integer.valueOf(v.substring(6, 8)); + int ms = getMillisInSecond(); + return (int) (h * DateTimeUtils.MILLIS_PER_HOUR + + m * DateTimeUtils.MILLIS_PER_MINUTE + + s * DateTimeUtils.MILLIS_PER_SECOND + + ms); + } + + private int getMillisInSecond() { + switch (v.length()) { + case 8: // "12:34:56" + return 0; + case 10: // "12:34:56.7" + return Integer.valueOf(v.substring(9)) * 100; + case 11: // "12:34:56.78" + return Integer.valueOf(v.substring(9)) * 10; + case 12: // "12:34:56.789" + default: // "12:34:56.7890000012345" + return Integer.valueOf(v.substring(9, 12)); + } + } + + public Calendar toCalendar() { + return Util.calendar(getMillisOfDay()); + } + + /** Converts this TimestampString to a string, truncated or padded with + * zeroes to a given precision. */ + public String toString(int precision) { + Preconditions.checkArgument(precision >= 0); + final int p = precision(); + if (precision < p) { + return round(precision).toString(precision); + } + if (precision > p) { + String s = v; + if (p == 0) { + s += "."; + } + return s + Strings.repeat("0", precision - p); + } + return v; + } + + private int precision() { + return v.length() < 9 ? 0 : (v.length() - 9); + } +} + +// End TimeString.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/TimestampString.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/TimestampString.java b/core/src/main/java/org/apache/calcite/util/TimestampString.java new file mode 100644 index 0000000..4e392f0 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/util/TimestampString.java @@ -0,0 +1,252 @@ +/* + * 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.util; + +import org.apache.calcite.avatica.util.DateTimeUtils; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import java.util.Calendar; +import java.util.regex.Pattern; + +/** + * Timestamp literal. + * + * <p>Immutable, internally represented as a string (in ISO format), + * and can support unlimited precision (milliseconds, nanoseconds). + */ +public class TimestampString implements Comparable<TimestampString> { + private static final Pattern PATTERN = + Pattern.compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" + + " " + + "[0-9][0-9]:[0-9][0-9]:[0-9][0-9](\\.[0-9]*[1-9])?"); + + final String v; + + /** Creates a TimeString. */ + public TimestampString(String v) { + this.v = v; + Preconditions.checkArgument(PATTERN.matcher(v).matches(), v); + } + + /** Creates a TimestampString for year, month, day, hour, minute, second, + * millisecond values. */ + public TimestampString(int year, int month, int day, int h, int m, int s) { + this(ymdhms(new StringBuilder(), year, month, day, h, m, s).toString()); + } + + /** Sets the fraction field of a {@code TimestampString} to a given number + * of milliseconds. Nukes the value set via {@link #withNanos}. + * + * <p>For example, + * {@code new TimestampString(1970, 1, 1, 2, 3, 4).withMillis(56)} + * yields {@code TIMESTAMP '1970-01-01 02:03:04.056'}. */ + public TimestampString withMillis(int millis) { + Preconditions.checkArgument(millis >= 0 && millis < 1000); + return withFraction(pad(3, millis)); + } + + /** Sets the fraction field of a {@code TimestampString} to a given number + * of nanoseconds. Nukes the value set via {@link #withMillis(int)}. + * + * <p>For example, + * {@code new TimestampString(1970, 1, 1, 2, 3, 4).withNanos(56789)} + * yields {@code TIMESTAMP '1970-01-01 02:03:04.000056789'}. */ + public TimestampString withNanos(int nanos) { + Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000); + return withFraction(pad(9, nanos)); + } + + /** Sets the fraction field of a {@code TimestampString}. + * The precision is determined by the number of leading zeros. + * Trailing zeros are stripped. + * + * <p>For example, + * {@code new TimestampString(1970, 1, 1, 2, 3, 4).withFraction("00506000")} + * yields {@code TIMESTAMP '1970-01-01 02:03:04.00506'}. */ + public TimestampString withFraction(String fraction) { + String v = this.v; + int i = v.indexOf('.'); + if (i >= 0) { + v = v.substring(0, i); + } + while (fraction.endsWith("0")) { + fraction = fraction.substring(0, fraction.length() - 1); + } + if (fraction.length() > 0) { + v = v + "." + fraction; + } + return new TimestampString(v); + } + + @Override public String toString() { + return v; + } + + @Override public boolean equals(Object o) { + // The value is in canonical form (no trailing zeros). + return o == this + || o instanceof TimestampString + && ((TimestampString) o).v.equals(v); + } + + @Override public int hashCode() { + return v.hashCode(); + } + + @Override public int compareTo(TimestampString o) { + return v.compareTo(o.v); + } + + static StringBuilder hms(StringBuilder b, int h, int m, int s) { + int2(b, h); + b.append(':'); + int2(b, m); + b.append(':'); + int2(b, s); + return b; + } + + static StringBuilder ymdhms(StringBuilder b, int year, int month, int day, + int h, int m, int s) { + ymd(b, year, month, day); + b.append(' '); + hms(b, h, m, s); + return b; + } + + static StringBuilder ymd(StringBuilder b, int year, int month, int day) { + int4(b, year); + b.append('-'); + int2(b, month); + b.append('-'); + int2(b, day); + return b; + } + + private static void int4(StringBuilder buf, int i) { + buf.append((char) ('0' + (i / 1000) % 10)); + buf.append((char) ('0' + (i / 100) % 10)); + buf.append((char) ('0' + (i / 10) % 10)); + buf.append((char) ('0' + i % 10)); + } + + private static void int2(StringBuilder buf, int i) { + buf.append((char) ('0' + (i / 10) % 10)); + buf.append((char) ('0' + i % 10)); + } + + /** Creates a TimestampString from a Calendar. */ + public static TimestampString fromCalendarFields(Calendar calendar) { + return new TimestampString( + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH), + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + calendar.get(Calendar.SECOND)) + .withMillis(calendar.get(Calendar.MILLISECOND)); + } + + public TimestampString round(int precision) { + Preconditions.checkArgument(precision >= 0); + int targetLength = 20 + precision; + if (v.length() <= targetLength) { + return this; + } + String v = this.v.substring(0, targetLength); + while (v.length() >= 20 && (v.endsWith("0") || v.endsWith("."))) { + v = v.substring(0, v.length() - 1); + } + return new TimestampString(v); + } + + /** Returns the number of milliseconds since the epoch. */ + public long getMillisSinceEpoch() { + final int year = Integer.valueOf(v.substring(0, 4)); + final int month = Integer.valueOf(v.substring(5, 7)); + final int day = Integer.valueOf(v.substring(8, 10)); + final int h = Integer.valueOf(v.substring(11, 13)); + final int m = Integer.valueOf(v.substring(14, 16)); + final int s = Integer.valueOf(v.substring(17, 19)); + final int ms = getMillisInSecond(); + final int d = DateTimeUtils.ymdToUnixDate(year, month, day); + return d * DateTimeUtils.MILLIS_PER_DAY + + h * DateTimeUtils.MILLIS_PER_HOUR + + m * DateTimeUtils.MILLIS_PER_MINUTE + + s * DateTimeUtils.MILLIS_PER_SECOND + + ms; + } + + private int getMillisInSecond() { + switch (v.length()) { + case 19: // "1999-12-31 12:34:56" + return 0; + case 21: // "1999-12-31 12:34:56.7" + return Integer.valueOf(v.substring(20)) * 100; + case 22: // "1999-12-31 12:34:56.78" + return Integer.valueOf(v.substring(20)) * 10; + case 23: // "1999-12-31 12:34:56.789" + default: // "1999-12-31 12:34:56.789123456" + return Integer.valueOf(v.substring(20, 23)); + } + } + + /** Creates a TimestampString that is a given number of milliseconds since + * the epoch. */ + public static TimestampString fromMillisSinceEpoch(long millis) { + return new TimestampString(DateTimeUtils.unixTimestampToString(millis)) + .withMillis((int) DateTimeUtils.floorMod(millis, 1000)); + } + + static String pad(int length, long v) { + StringBuilder s = new StringBuilder(Long.toString(v)); + while (s.length() < length) { + s.insert(0, "0"); + } + return s.toString(); + } + + public Calendar toCalendar() { + return Util.calendar(getMillisSinceEpoch()); + } + + /** Converts this TimestampString to a string, truncated or padded with + * zeroes to a given precision. */ + public String toString(int precision) { + Preconditions.checkArgument(precision >= 0); + final int p = precision(); + if (precision < p) { + return round(precision).toString(precision); + } + if (precision > p) { + String s = v; + if (p == 0) { + s += "."; + } + return s + Strings.repeat("0", precision - p); + } + return v; + } + + private int precision() { + return v.length() < 20 ? 0 : (v.length() - 20); + } +} + +// End TimestampString.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/Util.java ---------------------------------------------------------------------- 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 5e26fd7..e0746e2 100644 --- a/core/src/main/java/org/apache/calcite/util/Util.java +++ b/core/src/main/java/org/apache/calcite/util/Util.java @@ -665,7 +665,7 @@ public class Util { * underscore followed by the hex code of the character; and underscores are * doubled.</p> * - * Examples: + * <p>Examples: * * <ul> * <li><code>toJavaId("foo")</code> returns <code>"foo"</code> @@ -740,7 +740,7 @@ public class Util { /** * Converts a list of a string, with commas between elements. * - * For example, + * <p>For example, * <code>commaList(Arrays.asList({"a", "b"}))</code> * returns "a, b". * @@ -944,7 +944,7 @@ public class Util { * <pre><code>int x = Util.deprecated(0, false);</code></pre> * </blockquote> * - * but the usual usage is to pass in a descriptive string. + * <p>but the usual usage is to pass in a descriptive string. * * <h3>Examples</h3> * @@ -1265,7 +1265,7 @@ public class Util { * <blockquote>"std offset dst [offset],start[/time],end[/time]" * </blockquote> * - * where: + * <p>where: * * <ul> * <li>'std' specifies the abbrev of the time zone. @@ -1736,7 +1736,7 @@ public class Util { * print(i);<br> * }</code></blockquote> * - * will print 1, 2, 4. + * <p>will print 1, 2, 4. * * @param iterable Iterable * @param includeFilter Class whose instances to include @@ -2414,12 +2414,20 @@ public class Util { return reader(new FileInputStream(file)); } - /** Creates a {@link Calendar} in the GMT time zone and root locale. + /** Creates a {@link Calendar} in the UTC time zone and root locale. * Does not use the time zone or locale. */ public static Calendar calendar() { return Calendar.getInstance(DateTimeUtils.UTC_ZONE, Locale.ROOT); } + /** Creates a {@link Calendar} in the UTC time zone and root locale + * with a given time. */ + public static Calendar calendar(long millis) { + Calendar calendar = calendar(); + calendar.setTimeInMillis(millis); + return calendar; + } + //~ Inner Classes ---------------------------------------------------------- /** http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/ZonelessDate.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/ZonelessDate.java b/core/src/main/java/org/apache/calcite/util/ZonelessDate.java deleted file mode 100644 index 0d5fc16..0000000 --- a/core/src/main/java/org/apache/calcite/util/ZonelessDate.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.util; - -import org.apache.calcite.avatica.util.DateTimeUtils; - -import java.sql.Date; -import java.text.DateFormat; -import java.util.Calendar; -import java.util.TimeZone; - -/** - * ZonelessDate is a date value without a time zone. - */ -public class ZonelessDate extends ZonelessDatetime { - //~ Static fields/initializers --------------------------------------------- - - /** - * SerialVersionUID created with JDK 1.5 serialver tool. - */ - private static final long serialVersionUID = -6385775986251759394L; - - //~ Instance fields -------------------------------------------------------- - - protected transient Date tempDate; - - //~ Constructors ----------------------------------------------------------- - - /** - * Constructs a ZonelessDate. - */ - public ZonelessDate() { - } - - //~ Methods ---------------------------------------------------------------- - - // override ZonelessDatetime - public void setZonelessTime(long value) { - super.setZonelessTime(value); - clearTime(); - } - - // override ZonelessDatetime - public void setZonedTime(long value, TimeZone zone) { - super.setZonedTime(value, zone); - clearTime(); - } - - // implement ZonelessDatetime - public Object toJdbcObject() { - return new Date(getJdbcDate(DateTimeUtils.DEFAULT_ZONE)); - } - - /** - * Converts this ZonelessDate to a java.sql.Date and formats it via the - * {@link java.sql.Date#toString() toString()} method of that class. - * - * @return the formatted date string - */ - public String toString() { - Date jdbcDate = getTempDate(getJdbcDate(DateTimeUtils.DEFAULT_ZONE)); - return jdbcDate.toString(); - } - - /** - * Formats this ZonelessDate via a SimpleDateFormat. - * - * @param format Format string, as required by - * {@link java.text.SimpleDateFormat} - * @return the formatted date string - */ - public String toString(String format) { - DateFormat formatter = getFormatter(format); - Date jdbcDate = getTempDate(getTime()); - return formatter.format(jdbcDate); - } - - /** - * Parses a string as a ZonelessDate. - * - * @param s a string representing a date in ISO format, i.e. according to - * the SimpleDateFormat string "yyyy-MM-dd" - * @return the parsed date, or null if parsing failed - */ - public static ZonelessDate parse(String s) { - return parse(s, DateTimeUtils.DATE_FORMAT_STRING); - } - - /** - * Parses a string as a ZonelessDate with a given format string. - * - * @param s a string representing a date in ISO format, i.e. according to - * the SimpleDateFormat string "yyyy-MM-dd" - * @param format Format string as per {@link java.text.SimpleDateFormat} - * @return the parsed date, or null if parsing failed - */ - public static ZonelessDate parse(String s, String format) { - Calendar cal = - DateTimeUtils.parseDateFormat(s, format, DateTimeUtils.GMT_ZONE); - if (cal == null) { - return null; - } - ZonelessDate zd = new ZonelessDate(); - zd.setZonelessTime(cal.getTimeInMillis()); - return zd; - } - - /** - * Gets a temporary Date object. The same object is returned every time. - */ - protected Date getTempDate(long value) { - if (tempDate == null) { - tempDate = new Date(value); - } else { - tempDate.setTime(value); - } - return tempDate; - } -} - -// End ZonelessDate.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/ZonelessDatetime.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/ZonelessDatetime.java b/core/src/main/java/org/apache/calcite/util/ZonelessDatetime.java deleted file mode 100644 index ab311e7..0000000 --- a/core/src/main/java/org/apache/calcite/util/ZonelessDatetime.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.util; - -import org.apache.calcite.avatica.util.DateTimeUtils; - -import java.io.Serializable; -import java.text.DateFormat; -import java.util.Calendar; -import java.util.Locale; -import java.util.TimeZone; - -/** - * ZonelessDatetime is an abstract class for dates, times, or timestamps that - * contain a zoneless time value. - */ -public abstract class ZonelessDatetime implements BasicDatetime, Serializable { - //~ Static fields/initializers --------------------------------------------- - - /** - * SerialVersionUID created with JDK 1.5 serialver tool. - */ - private static final long serialVersionUID = -1274713852537224763L; - - //~ Instance fields -------------------------------------------------------- - - /** - * Treat this as a protected field. It is only made public to simplify Java - * code generation. - */ - public long internalTime; - - // The following fields are workspace and are not serialized. - - protected transient Calendar tempCal; - protected transient DateFormat tempFormatter; - protected transient String lastFormat; - - //~ Methods ---------------------------------------------------------------- - - // implement BasicDatetime - public long getTime() { - return internalTime; - } - - // implement BasicDatetime - public void setZonelessTime(long value) { - this.internalTime = value; - } - - // implement BasicDatetime - public void setZonedTime(long value, TimeZone zone) { - this.internalTime = value + zone.getOffset(value); - } - - /** - * Gets the time portion of this zoneless datetime. - */ - public long getTimeValue() { - // Value must be non-negative, even for negative timestamps, and - // unfortunately the '%' operator returns a negative value if its LHS - // is negative. - long timePart = internalTime % DateTimeUtils.MILLIS_PER_DAY; - if (timePart < 0) { - timePart += DateTimeUtils.MILLIS_PER_DAY; - } - return timePart; - } - - /** - * Gets the date portion of this zoneless datetime. - */ - public long getDateValue() { - return internalTime - getTimeValue(); - } - - /** - * Clears the date component of this datetime - */ - public void clearDate() { - internalTime = getTimeValue(); - } - - /** - * Clears the time component of this datetime - */ - public void clearTime() { - internalTime = getDateValue(); - } - - /** - * Gets the value of this datetime as a milliseconds value for - * {@link java.sql.Time}. - * - * @param zone time zone in which to generate a time value for - */ - public long getJdbcTime(TimeZone zone) { - long timeValue = getTimeValue(); - return timeValue - zone.getOffset(timeValue); - } - - /** - * Gets the value of this datetime as a milliseconds value for - * {@link java.sql.Date}. - * - * @param zone time zone in which to generate a time value for - */ - public long getJdbcDate(TimeZone zone) { - Calendar cal = getCalendar(DateTimeUtils.GMT_ZONE); - cal.setTimeInMillis(getDateValue()); - - int year = cal.get(Calendar.YEAR); - int doy = cal.get(Calendar.DAY_OF_YEAR); - - cal.clear(); - cal.setTimeZone(zone); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.DAY_OF_YEAR, doy); - return cal.getTimeInMillis(); - } - - /** - * Gets the value of this datetime as a milliseconds value for - * {@link java.sql.Timestamp}. - * - * @param zone time zone in which to generate a time value for - */ - public long getJdbcTimestamp(TimeZone zone) { - Calendar cal = getCalendar(DateTimeUtils.GMT_ZONE); - cal.setTimeInMillis(internalTime); - - int year = cal.get(Calendar.YEAR); - int doy = cal.get(Calendar.DAY_OF_YEAR); - int hour = cal.get(Calendar.HOUR_OF_DAY); - int minute = cal.get(Calendar.MINUTE); - int second = cal.get(Calendar.SECOND); - int millis = cal.get(Calendar.MILLISECOND); - - cal.clear(); - cal.setTimeZone(zone); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.DAY_OF_YEAR, doy); - cal.set(Calendar.HOUR_OF_DAY, hour); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - cal.set(Calendar.MILLISECOND, millis); - return cal.getTimeInMillis(); - } - - /** - * Returns this datetime as a Jdbc object - */ - public abstract Object toJdbcObject(); - - /** - * Gets a temporary Calendar set to the specified time zone. The same - * Calendar is returned on subsequent calls. - */ - protected Calendar getCalendar(TimeZone zone) { - if (tempCal == null) { - tempCal = Calendar.getInstance(zone, Locale.ROOT); - } else { - tempCal.setTimeZone(zone); - } - return tempCal; - } - - /** - * Gets a temporary formatter for a zoneless date time. The same formatter - * is returned on subsequent calls. - * - * @param format a {@link java.text.SimpleDateFormat} format string - */ - protected DateFormat getFormatter(String format) { - if ((tempFormatter != null) && lastFormat.equals(format)) { - return tempFormatter; - } - tempFormatter = DateTimeUtils.newDateFormat(format); - tempFormatter.setTimeZone(DateTimeUtils.GMT_ZONE); - lastFormat = format; - return tempFormatter; - } -} - -// End ZonelessDatetime.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/ZonelessTime.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/ZonelessTime.java b/core/src/main/java/org/apache/calcite/util/ZonelessTime.java deleted file mode 100644 index 28a788c..0000000 --- a/core/src/main/java/org/apache/calcite/util/ZonelessTime.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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.util; - -import org.apache.calcite.avatica.util.DateTimeUtils; - -import java.sql.Time; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.TimeZone; - -/** - * ZonelessTime is a time value without a time zone. - */ -public class ZonelessTime extends ZonelessDatetime { - //~ Static fields/initializers --------------------------------------------- - - /** - * SerialVersionUID created with JDK 1.5 serialver tool. - */ - private static final long serialVersionUID = 906156904798141861L; - - //~ Instance fields -------------------------------------------------------- - - protected final int precision; - protected transient Time tempTime; - - //~ Constructors ----------------------------------------------------------- - - /** - * Constructs a ZonelessTime - */ - public ZonelessTime() { - precision = 0; - } - - /** - * Constructs a ZonelessTime with precision. - * - * <p>The precision is the number of digits to the right of the decimal - * point in the seconds value. For example, a <code>TIME(6)</code> has a - * precision to microseconds. - * - * @param precision Number of digits of precision - */ - public ZonelessTime(int precision) { - this.precision = precision; - } - - //~ Methods ---------------------------------------------------------------- - - // override ZonelessDatetime - public void setZonelessTime(long value) { - super.setZonelessTime(value); - clearDate(); - } - - // override ZonelessDatetime - public void setZonedTime(long value, TimeZone zone) { - super.setZonedTime(value, zone); - clearDate(); - } - - // implement ZonelessDatetime - public Object toJdbcObject() { - return new Time(getJdbcTime(DateTimeUtils.DEFAULT_ZONE)); - } - - /** - * Override ZonelessDatetime. - * - * <p>NOTE: the returned timestamp is based on the current date of the - * specified time zone, rather than the context variable for current_date, - * as specified by the SQL standard. - */ - public long getJdbcTimestamp(TimeZone zone) { - Calendar cal = getCalendar(DateTimeUtils.GMT_ZONE); - cal.setTimeInMillis(getTime()); - int hour = cal.get(Calendar.HOUR_OF_DAY); - int minute = cal.get(Calendar.MINUTE); - int second = cal.get(Calendar.SECOND); - int millis = cal.get(Calendar.MILLISECOND); - - cal.setTimeZone(zone); - cal.setTimeInMillis(System.currentTimeMillis()); - cal.set(Calendar.HOUR_OF_DAY, hour); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - cal.set(Calendar.MILLISECOND, millis); - return cal.getTimeInMillis(); - } - - /** - * Converts this ZonelessTime to a java.sql.Time and formats it via the - * {@link java.sql.Time#toString() toString()} method of that class. - * - * @return the formatted time string - */ - public String toString() { - Time jdbcTime = getTempTime(getJdbcTime(DateTimeUtils.DEFAULT_ZONE)); - return jdbcTime.toString(); - } - - /** - * Formats this ZonelessTime via a SimpleDateFormat - * - * @param format format string, as required by SimpleDateFormat - * @return the formatted time string - */ - public String toString(String format) { - DateFormat formatter = getFormatter(format); - Time jdbcTime = getTempTime(getTime()); - return formatter.format(jdbcTime); - } - - /** - * Parses a string as a ZonelessTime. - * - * @param s a string representing a time in ISO format, i.e. according to - * the {@link SimpleDateFormat} string "HH:mm:ss" - * @return the parsed time, or null if parsing failed - */ - public static ZonelessTime parse(String s) { - return parse(s, DateTimeUtils.TIME_FORMAT_STRING); - } - - /** - * Parses a string as a ZonelessTime using a given format string. - * - * @param s a string representing a time the given format - * @param format format string as per {@link SimpleDateFormat} - * @return the parsed time, or null if parsing failed - */ - public static ZonelessTime parse(String s, String format) { - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral(s, - format, - DateTimeUtils.GMT_ZONE); - if (pt == null) { - return null; - } - ZonelessTime zt = new ZonelessTime(pt.getPrecision()); - zt.setZonelessTime(pt.getCalendar().getTime().getTime()); - return zt; - } - - /** - * Gets a temporary Time object. The same object is returned every time. - */ - protected Time getTempTime(long value) { - if (tempTime == null) { - tempTime = new Time(value); - } else { - tempTime.setTime(value); - } - return tempTime; - } -} - -// End ZonelessTime.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/ZonelessTimestamp.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/ZonelessTimestamp.java b/core/src/main/java/org/apache/calcite/util/ZonelessTimestamp.java deleted file mode 100644 index e58f687..0000000 --- a/core/src/main/java/org/apache/calcite/util/ZonelessTimestamp.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.util; - -import org.apache.calcite.avatica.util.DateTimeUtils; - -import java.sql.Timestamp; -import java.text.DateFormat; - -/** - * ZonelessTimestamp is a timestamp value without a time zone. - */ -public class ZonelessTimestamp extends ZonelessDatetime { - //~ Static fields/initializers --------------------------------------------- - - /** - * SerialVersionUID created with JDK 1.5 serialver tool. - */ - private static final long serialVersionUID = -6829714640541648394L; - - //~ Instance fields -------------------------------------------------------- - - protected final int precision; - - protected transient Timestamp tempTimestamp; - - //~ Constructors ----------------------------------------------------------- - - /** - * Constructs a ZonelessTimestamp. - */ - public ZonelessTimestamp() { - this.precision = 0; - } - - /** - * Constructs a ZonelessTimestamp with precision. - * - * <p>The precision is the number of digits to the right of the decimal - * point in the seconds value. For example, a <code>TIMESTAMP(3)</code> has - * a precision to milliseconds. - * - * @param precision Number of digits of precision - */ - public ZonelessTimestamp(int precision) { - this.precision = precision; - } - - //~ Methods ---------------------------------------------------------------- - - // implement ZonelessDatetime - public Object toJdbcObject() { - return new Timestamp(getJdbcTimestamp(DateTimeUtils.DEFAULT_ZONE)); - } - - /** - * Converts this ZonelessTimestamp to a java.sql.Timestamp and formats it - * via the {@link java.sql.Timestamp#toString() toString()} method of that - * class. - * - * <p>Note: Jdbc formatting always includes a decimal point and at least one - * digit of milliseconds precision. Trailing zeros, except for the first one - * after the decimal point, do not appear in the output. - * - * @return the formatted time string - */ - public String toString() { - Timestamp ts = - getTempTimestamp(getJdbcTimestamp(DateTimeUtils.DEFAULT_ZONE)); - - // Remove trailing '.0' so that format is consistent with SQL spec for - // CAST(TIMESTAMP(0) TO VARCHAR). E.g. "1969-12-31 16:00:00.0" - // becomes "1969-12-31 16:00:00" - String sts = ts.toString(); - if (sts.length() > 19 && ts.getNanos() == 0) { - sts = sts.substring(0, 19); - } - return sts; - } - - /** - * Formats this ZonelessTimestamp via a SimpleDateFormat. This method does - * not display milliseconds precision. - * - * @param format format string, as required by SimpleDateFormat - * @return the formatted timestamp string - */ - public String toString(String format) { - DateFormat formatter = getFormatter(format); - Timestamp ts = getTempTimestamp(getTime()); - return formatter.format(ts); - } - - /** - * Parses a string as a ZonelessTimestamp. - * - * <p>This method's parsing is strict and may parse fractional seconds (as - * opposed to just milliseconds.) - * - * @param s a string representing a time in ISO format, i.e. according to - * the SimpleDateFormat string "yyyy-MM-dd HH:mm:ss" - * @return the parsed time, or null if parsing failed - */ - public static ZonelessTimestamp parse(String s) { - return parse(s, DateTimeUtils.TIMESTAMP_FORMAT_STRING); - } - - /** - * Parses a string as a ZonelessTimestamp using a given format string. - * - * <p>This method's parsing is strict and may parse fractional seconds (as - * opposed to just milliseconds.) - * - * @param s a string representing a time in ISO format, i.e. according to - * the SimpleDateFormat string "yyyy-MM-dd HH:mm:ss" - * @param format Format string as per {@link java.text.SimpleDateFormat} - * @return the parsed timestamp, or null if parsing failed - */ - public static ZonelessTimestamp parse(String s, String format) { - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral(s, - format, - DateTimeUtils.GMT_ZONE); - if (pt == null) { - return null; - } - ZonelessTimestamp zt = new ZonelessTimestamp(pt.getPrecision()); - zt.setZonelessTime(pt.getCalendar().getTime().getTime()); - return zt; - } - - /** - * Gets a temporary Timestamp object. The same object is returned every - * time. - */ - protected Timestamp getTempTimestamp(long value) { - if (tempTimestamp == null) { - tempTimestamp = new Timestamp(value); - } else { - tempTimestamp.setTime(value); - } - return tempTimestamp; - } -} - -// End ZonelessTimestamp.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java index 8f5015c..4e4db08 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java @@ -659,6 +659,26 @@ public class RelToSqlConverterTest { sql(query).ok(expected); } + @Test public void testLiteral() { + checkLiteral("DATE '1978-05-02'"); + checkLiteral("TIME '12:34:56'"); + checkLiteral("TIME '12:34:56.78'"); + checkLiteral("TIMESTAMP '1978-05-02 12:34:56.78'"); + checkLiteral("'I can''t explain'"); + checkLiteral("''"); + checkLiteral("TRUE"); + checkLiteral("123"); + checkLiteral("123.45"); + checkLiteral("-123.45"); + } + + private void checkLiteral(String s) { + sql("VALUES " + s) + .dialect(DatabaseProduct.HSQLDB.getDialect()) + .ok("SELECT *\n" + + "FROM (VALUES (" + s + "))"); + } + /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-1798">[CALCITE-1798] * Generate dialect-specific SQL for FLOOR operator</a>. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java ---------------------------------------------------------------------- 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 bbf95f0..0fd99f8 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java @@ -16,18 +16,28 @@ */ package org.apache.calcite.rex; +import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.sql.type.SqlTypeFactoryImpl; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.DateString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; +import org.apache.calcite.util.Util; import org.junit.Test; +import java.util.Calendar; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; /** - * Test for {@link RexBuilder} + * Test for {@link RexBuilder}. */ public class RexBuilderTest { @@ -80,6 +90,203 @@ public class RexBuilderTest { assertEquals(ensuredNode.getType(), typeFactory.createSqlType(SqlTypeName.INTEGER)); } + private static final long MOON = -14159025000L; + + private static final int MOON_DAY = -164; + + private static final int MOON_TIME = 10575000; + + /** Tests {@link RexBuilder#makeTimestampLiteral(TimestampString, int)}. */ + @Test public void testTimestampLiteral() { + final RelDataTypeFactory typeFactory = + new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); + final RelDataType timestampType = + typeFactory.createSqlType(SqlTypeName.TIMESTAMP); + final RelDataType timestampType3 = + typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 3); + final RelDataType timestampType9 = + typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 9); + final RelDataType timestampType18 = + typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 18); + final RexBuilder builder = new RexBuilder(typeFactory); + + // Old way: provide a Calendar + final Calendar calendar = Util.calendar(); + calendar.set(1969, Calendar.JULY, 21, 2, 56, 15); // one small step + calendar.set(Calendar.MILLISECOND, 0); + checkTimestamp(builder.makeLiteral(calendar, timestampType, false)); + + // Old way #2: Provide a Long + checkTimestamp(builder.makeLiteral(MOON, timestampType, false)); + + // The new way + final TimestampString ts = new TimestampString(1969, 7, 21, 2, 56, 15); + checkTimestamp(builder.makeLiteral(ts, timestampType, false)); + + // Now with milliseconds + final TimestampString ts2 = ts.withMillis(56); + assertThat(ts2.toString(), is("1969-07-21 02:56:15.056")); + final RexNode literal2 = builder.makeLiteral(ts2, timestampType3, false); + assertThat(((RexLiteral) literal2).getValueAs(TimestampString.class) + .toString(), is("1969-07-21 02:56:15.056")); + + // Now with nanoseconds + final TimestampString ts3 = ts.withNanos(56); + final RexNode literal3 = builder.makeLiteral(ts3, timestampType9, false); + assertThat(((RexLiteral) literal3).getValueAs(TimestampString.class) + .toString(), is("1969-07-21 02:56:15")); + final TimestampString ts3b = ts.withNanos(2345678); + final RexNode literal3b = builder.makeLiteral(ts3b, timestampType9, false); + assertThat(((RexLiteral) literal3b).getValueAs(TimestampString.class) + .toString(), is("1969-07-21 02:56:15.002")); + + // Now with a very long fraction + final TimestampString ts4 = ts.withFraction("102030405060708090102"); + final RexNode literal4 = builder.makeLiteral(ts4, timestampType18, false); + assertThat(((RexLiteral) literal4).getValueAs(TimestampString.class) + .toString(), is("1969-07-21 02:56:15.102")); + + // toString + assertThat(ts2.round(1).toString(), is("1969-07-21 02:56:15")); + assertThat(ts2.round(2).toString(), is("1969-07-21 02:56:15.05")); + assertThat(ts2.round(3).toString(), is("1969-07-21 02:56:15.056")); + assertThat(ts2.round(4).toString(), is("1969-07-21 02:56:15.056")); + + assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000")); + assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0")); + assertThat(ts2.toString(0), is("1969-07-21 02:56:15")); + + assertThat(ts2.round(0).toString(), is("1969-07-21 02:56:15")); + assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15")); + assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0")); + assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00")); + + assertThat(TimestampString.fromMillisSinceEpoch(1456513560123L).toString(), + is("2016-02-26 19:06:00.123")); + } + + private void checkTimestamp(RexNode node) { + assertThat(node.toString(), is("1969-07-21 02:56:15")); + RexLiteral literal = (RexLiteral) node; + assertThat(literal.getValue() instanceof Calendar, is(true)); + assertThat(literal.getValue2() instanceof Long, is(true)); + assertThat(literal.getValue3() instanceof Long, is(true)); + assertThat((Long) literal.getValue2(), is(MOON)); + assertThat(literal.getValueAs(Calendar.class), notNullValue()); + assertThat(literal.getValueAs(TimestampString.class), notNullValue()); + } + + /** Tests {@link RexBuilder#makeTimeLiteral(TimeString, int)}. */ + @Test public void testTimeLiteral() { + final RelDataTypeFactory typeFactory = + new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); + RelDataType timeType = typeFactory.createSqlType(SqlTypeName.TIME); + final RelDataType timeType3 = + typeFactory.createSqlType(SqlTypeName.TIME, 3); + final RelDataType timeType9 = + typeFactory.createSqlType(SqlTypeName.TIME, 9); + final RelDataType timeType18 = + typeFactory.createSqlType(SqlTypeName.TIME, 18); + final RexBuilder builder = new RexBuilder(typeFactory); + + // Old way: provide a Calendar + final Calendar calendar = Util.calendar(); + calendar.set(1969, Calendar.JULY, 21, 2, 56, 15); // one small step + calendar.set(Calendar.MILLISECOND, 0); + checkTime(builder.makeLiteral(calendar, timeType, false)); + + // Old way #2: Provide a Long + checkTime(builder.makeLiteral(MOON_TIME, timeType, false)); + + // The new way + final TimeString t = new TimeString(2, 56, 15); + assertThat(t.getMillisOfDay(), is(10575000)); + checkTime(builder.makeLiteral(t, timeType, false)); + + // Now with milliseconds + final TimeString t2 = t.withMillis(56); + assertThat(t2.getMillisOfDay(), is(10575056)); + assertThat(t2.toString(), is("02:56:15.056")); + final RexNode literal2 = builder.makeLiteral(t2, timeType3, false); + assertThat(((RexLiteral) literal2).getValueAs(TimeString.class) + .toString(), is("02:56:15.056")); + + // Now with nanoseconds + final TimeString t3 = t.withNanos(2345678); + assertThat(t3.getMillisOfDay(), is(10575002)); + final RexNode literal3 = builder.makeLiteral(t3, timeType9, false); + assertThat(((RexLiteral) literal3).getValueAs(TimeString.class) + .toString(), is("02:56:15.002")); + + // Now with a very long fraction + final TimeString t4 = t.withFraction("102030405060708090102"); + assertThat(t4.getMillisOfDay(), is(10575102)); + final RexNode literal4 = builder.makeLiteral(t4, timeType18, false); + assertThat(((RexLiteral) literal4).getValueAs(TimeString.class) + .toString(), is("02:56:15.102")); + + // toString + assertThat(t2.round(1).toString(), is("02:56:15")); + assertThat(t2.round(2).toString(), is("02:56:15.05")); + assertThat(t2.round(3).toString(), is("02:56:15.056")); + assertThat(t2.round(4).toString(), is("02:56:15.056")); + + assertThat(t2.toString(6), is("02:56:15.056000")); + assertThat(t2.toString(1), is("02:56:15.0")); + assertThat(t2.toString(0), is("02:56:15")); + + assertThat(t2.round(0).toString(), is("02:56:15")); + assertThat(t2.round(0).toString(0), is("02:56:15")); + assertThat(t2.round(0).toString(1), is("02:56:15.0")); + assertThat(t2.round(0).toString(2), is("02:56:15.00")); + + assertThat(TimeString.fromMillisOfDay(53560123).toString(), + is("14:52:40.123")); + } + + private void checkTime(RexNode node) { + assertThat(node.toString(), is("02:56:15")); + RexLiteral literal = (RexLiteral) node; + assertThat(literal.getValue() instanceof Calendar, is(true)); + assertThat(literal.getValue2() instanceof Integer, is(true)); + assertThat(literal.getValue3() instanceof Integer, is(true)); + assertThat((Integer) literal.getValue2(), is(MOON_TIME)); + assertThat(literal.getValueAs(Calendar.class), notNullValue()); + assertThat(literal.getValueAs(TimeString.class), notNullValue()); + } + + /** Tests {@link RexBuilder#makeDateLiteral(DateString)}. */ + @Test public void testDateLiteral() { + final RelDataTypeFactory typeFactory = + new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); + RelDataType dateType = typeFactory.createSqlType(SqlTypeName.DATE); + final RexBuilder builder = new RexBuilder(typeFactory); + + // Old way: provide a Calendar + final Calendar calendar = Util.calendar(); + calendar.set(1969, Calendar.JULY, 21); // one small step + calendar.set(Calendar.MILLISECOND, 0); + checkDate(builder.makeLiteral(calendar, dateType, false)); + + // Old way #2: Provide in Integer + checkDate(builder.makeLiteral(MOON_DAY, dateType, false)); + + // The new way + final DateString d = new DateString(1969, 7, 21); + checkDate(builder.makeLiteral(d, dateType, false)); + } + + private void checkDate(RexNode node) { + assertThat(node.toString(), is("1969-07-21")); + RexLiteral literal = (RexLiteral) node; + assertThat(literal.getValue() instanceof Calendar, is(true)); + assertThat(literal.getValue2() instanceof Integer, is(true)); + assertThat(literal.getValue3() instanceof Integer, is(true)); + assertThat((Integer) literal.getValue2(), is(MOON_DAY)); + assertThat(literal.getValueAs(Calendar.class), notNullValue()); + assertThat(literal.getValueAs(DateString.class), notNullValue()); + } + } // End RexBuilderTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java index 46190df..d421033 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java @@ -36,6 +36,7 @@ import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.tools.Frameworks; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Util; @@ -47,7 +48,6 @@ import org.junit.Test; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; import java.util.Random; @@ -159,20 +159,22 @@ public class RexExecutorTest { checkConstant(true, new Function<RexBuilder, RexNode>() { public RexNode apply(RexBuilder rexBuilder) { - Calendar calendar = Util.calendar(); + final DateString d = + DateString.fromCalendarFields(Util.calendar()); return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, - rexBuilder.makeDateLiteral(calendar), - rexBuilder.makeDateLiteral(calendar)); + rexBuilder.makeDateLiteral(d), + rexBuilder.makeDateLiteral(d)); } }); // date 'today' < date 'today' -> false checkConstant(false, new Function<RexBuilder, RexNode>() { public RexNode apply(RexBuilder rexBuilder) { - Calendar calendar = Util.calendar(); + final DateString d = + DateString.fromCalendarFields(Util.calendar()); return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, - rexBuilder.makeDateLiteral(calendar), - rexBuilder.makeDateLiteral(calendar)); + rexBuilder.makeDateLiteral(d), + rexBuilder.makeDateLiteral(d)); } }); } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java index f4baa86..95d70d3 100644 --- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java +++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java @@ -2230,13 +2230,13 @@ public class SqlParserTest { checkExpSame("TIMESTAMP '2004-06-01 15:55:55.900'"); checkExp( "TIMESTAMP '2004-06-01 15:55:55.1234'", - "TIMESTAMP '2004-06-01 15:55:55.123'"); + "TIMESTAMP '2004-06-01 15:55:55.1234'"); checkExp( "TIMESTAMP '2004-06-01 15:55:55.1236'", - "TIMESTAMP '2004-06-01 15:55:55.123'"); + "TIMESTAMP '2004-06-01 15:55:55.1236'"); checkExp( "TIMESTAMP '2004-06-01 15:55:55.9999'", - "TIMESTAMP '2004-06-01 15:55:55.999'"); + "TIMESTAMP '2004-06-01 15:55:55.9999'"); checkExpSame("NULL"); } @@ -3690,6 +3690,7 @@ public class SqlParserTest { checkExp("TIME '12:01:01.'", "TIME '12:01:01'"); checkExp("TIME '12:01:01.000'", "TIME '12:01:01.000'"); checkExp("TIME '12:01:01.001'", "TIME '12:01:01.001'"); + checkExp("TIME '12:01:01.01023456789'", "TIME '12:01:01.01023456789'"); // Timestamp literals checkExp( @@ -3701,6 +3702,10 @@ public class SqlParserTest { checkExp( "TIMESTAMP '2004-12-01 12:01:01.'", "TIMESTAMP '2004-12-01 12:01:01'"); + checkExp( + "TIMESTAMP '2004-12-01 12:01:01.010234567890'", + "TIMESTAMP '2004-12-01 12:01:01.010234567890'"); + checkExpSame("TIMESTAMP '2004-12-01 12:01:01.01023456789'"); // Failures. checkFails("^DATE '12/21/99'^", "(?s).*Illegal DATE literal.*"); http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java index 63443cb..d3e19dc 100644 --- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java +++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java @@ -51,6 +51,7 @@ import org.apache.calcite.test.SqlLimitsTest; import org.apache.calcite.util.Bug; import org.apache.calcite.util.Holder; import org.apache.calcite.util.Pair; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.base.Function; @@ -6883,9 +6884,8 @@ public abstract class SqlOperatorBaseTest { case VARCHAR: return SqlLiteral.createCharString(value.toString(), SqlParserPos.ZERO); case TIMESTAMP: - Calendar calendar = Util.calendar(); - calendar.setTimeInMillis((Long) value); - return SqlLiteral.createTimestamp(calendar, type.getPrecision(), + TimestampString ts = TimestampString.fromMillisSinceEpoch((Long) value); + return SqlLiteral.createTimestamp(ts, type.getPrecision(), SqlParserPos.ZERO); default: throw new AssertionError(type); http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java index ee2511d..f089170 100644 --- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java @@ -36,8 +36,11 @@ import org.apache.calcite.server.CalciteServerStatement; import org.apache.calcite.sql.SqlCollation; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.tools.Frameworks; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.Holder; import org.apache.calcite.util.NlsString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import org.junit.Ignore; @@ -50,7 +53,6 @@ import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; -import java.util.Calendar; /** * Unit tests for {@link RexImplicationChecker}. @@ -179,9 +181,9 @@ public class RexImplicationCheckerTest { @Ignore("work in progress") @Test public void testSimpleDate() { final Fixture f = new Fixture(); - final Calendar instance = Util.calendar(); - final RexNode node1 = f.ge(f.dt, f.rexBuilder.makeDateLiteral(instance)); - final RexNode node2 = f.eq(f.dt, f.rexBuilder.makeDateLiteral(instance)); + final DateString d = DateString.fromCalendarFields(Util.calendar()); + final RexNode node1 = f.ge(f.dt, f.rexBuilder.makeDateLiteral(d)); + final RexNode node2 = f.eq(f.dt, f.rexBuilder.makeDateLiteral(d)); f.checkImplies(node2, node1); f.checkNotImplies(node1, node2); @@ -190,9 +192,10 @@ public class RexImplicationCheckerTest { @Ignore("work in progress") @Test public void testSimpleTimeStamp() { final Fixture f = new Fixture(); - final Calendar calendar = Util.calendar(); - final RexNode node1 = f.le(f.ts, f.timestampLiteral(calendar)); - final RexNode node2 = f.le(f.ts, f.timestampLiteral(calendar)); + final TimestampString ts = + TimestampString.fromCalendarFields(Util.calendar()); + final RexNode node1 = f.le(f.ts, f.timestampLiteral(ts)); + final RexNode node2 = f.le(f.ts, f.timestampLiteral(ts)); f.checkImplies(node1, node2); f.checkNotImplies(node2, node1); @@ -201,9 +204,9 @@ public class RexImplicationCheckerTest { @Ignore("work in progress") @Test public void testSimpleTime() { final Fixture f = new Fixture(); - final Calendar calendar = Util.calendar(); - final RexNode node1 = f.le(f.ts, f.timeLiteral(calendar)); - final RexNode node2 = f.le(f.ts, f.timeLiteral(calendar)); + final TimeString t = TimeString.fromCalendarFields(Util.calendar()); + final RexNode node1 = f.le(f.ts, f.timeLiteral(t)); + final RexNode node2 = f.le(f.ts, f.timeLiteral(t)); f.checkImplies(node1, node2); f.checkNotImplies(node2, node1); @@ -491,14 +494,13 @@ public class RexImplicationCheckerTest { new NlsString(z, null, SqlCollation.COERCIBLE)); } - public RexNode timestampLiteral(Calendar calendar) { - return rexBuilder.makeTimestampLiteral( - calendar, timeStampDataType.getPrecision()); + public RexNode timestampLiteral(TimestampString ts) { + return rexBuilder.makeTimestampLiteral(ts, + timeStampDataType.getPrecision()); } - public RexNode timeLiteral(Calendar calendar) { - return rexBuilder.makeTimestampLiteral( - calendar, timeDataType.getPrecision()); + public RexNode timeLiteral(TimeString t) { + return rexBuilder.makeTimeLiteral(t, timeDataType.getPrecision()); } public RexNode cast(RelDataType type, RexNode exp) { http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/java/org/apache/calcite/test/RexProgramTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java index 408cb73..f889347 100644 --- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java @@ -38,9 +38,12 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeAssignmentRules; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.TestUtil; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.collect.ImmutableList; @@ -1397,12 +1400,11 @@ public class RexProgramTest { literals.add((RexLiteral) rexBuilder.makeLiteral(new ByteString(new byte[] {1, 2, -34, 0, -128}), typeFactory.createSqlType(SqlTypeName.BINARY, 5), false)); - literals.add( - rexBuilder.makeDateLiteral(cal(1974, Calendar.AUGUST, 9, 0, 0, 0))); - literals.add(rexBuilder.makeTimeLiteral(cal(0, 0, 0, 1, 23, 45), 0)); + literals.add(rexBuilder.makeDateLiteral(new DateString(1974, 8, 9))); + literals.add(rexBuilder.makeTimeLiteral(new TimeString(1, 23, 45), 0)); literals.add( rexBuilder.makeTimestampLiteral( - cal(1974, Calendar.AUGUST, 9, 1, 23, 45), 0)); + new TimestampString(1974, 8, 9, 1, 23, 45), 0)); final Multimap<SqlTypeName, RexLiteral> map = LinkedHashMultimap.create(); for (RexLiteral literal : literals) { http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/test/resources/sql/misc.iq ---------------------------------------------------------------------- diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq index cbf12fe..9ecd55d 100644 --- a/core/src/test/resources/sql/misc.iq +++ b/core/src/test/resources/sql/misc.iq @@ -1910,7 +1910,7 @@ select TIMESTAMP '2016-02-26 19:06:00.123456789', (1 row) !ok -EnumerableCalc(expr#0=[{inputs}], expr#1=[2016-02-26 19:06:00.123], expr#2=[2016-02-26 19:06:00], expr#3=[2016-02-26 19:06:00.1], expr#4=[2016-02-26 19:06:00.12], expr#5=[2016-02-26 19:06:00.123], expr#6=[2016-02-26 19:06:00.123], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t2], EXPR$3=[$t3], EXPR$4=[$t4], EXPR$5=[$t1], EXPR$6=[$t5], EXPR$7=[$t6]) +EnumerableCalc(expr#0=[{inputs}], expr#1=[2016-02-26 19:06:00.123], expr#2=[2016-02-26 19:06:00], expr#3=[2016-02-26 19:06:00.1], expr#4=[2016-02-26 19:06:00.12], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t2], EXPR$3=[$t3], EXPR$4=[$t4], EXPR$5=[$t1], EXPR$6=[$t1], EXPR$7=[$t1]) EnumerableValues(tuples=[[{ 0 }]]) !plan http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java index 0e2b3d3..d6065e2 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java @@ -24,6 +24,8 @@ import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.DateString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import org.apache.calcite.util.trace.CalciteTrace; @@ -37,9 +39,7 @@ import com.google.common.collect.TreeRangeSet; import org.slf4j.Logger; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; -import java.util.regex.Pattern; /** * Utilities for generating intervals from RexNode. @@ -49,10 +49,6 @@ public class DruidDateTimeUtils { protected static final Logger LOGGER = CalciteTrace.getPlannerTracer(); - private static final Pattern TIMESTAMP_PATTERN = - Pattern.compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" - + " [0-9][0-9]:[0-9][0-9]:[0-9][0-9]"); - private DruidDateTimeUtils() { } @@ -63,7 +59,7 @@ public class DruidDateTimeUtils { */ public static List<LocalInterval> createInterval(RelDataType type, RexNode e) { - final List<Range<Calendar>> ranges = extractRanges(e, false); + final List<Range<TimestampString>> ranges = extractRanges(e, false); if (ranges == null) { // We did not succeed, bail out return null; @@ -78,18 +74,18 @@ public class DruidDateTimeUtils { return toInterval(ImmutableList.<Range>copyOf(condensedRanges.asRanges())); } - protected static List<LocalInterval> toInterval(List<Range<Calendar>> ranges) { + protected static List<LocalInterval> toInterval(List<Range<TimestampString>> ranges) { List<LocalInterval> intervals = Lists.transform(ranges, - new Function<Range<Calendar>, LocalInterval>() { - public LocalInterval apply(Range<Calendar> range) { + new Function<Range<TimestampString>, LocalInterval>() { + public LocalInterval apply(Range<TimestampString> range) { if (!range.hasLowerBound() && !range.hasUpperBound()) { return DruidTable.DEFAULT_INTERVAL; } long start = range.hasLowerBound() - ? range.lowerEndpoint().getTime().getTime() + ? range.lowerEndpoint().getMillisSinceEpoch() : DruidTable.DEFAULT_INTERVAL.getStartMillis(); long end = range.hasUpperBound() - ? range.upperEndpoint().getTime().getTime() + ? range.upperEndpoint().getMillisSinceEpoch() : DruidTable.DEFAULT_INTERVAL.getEndMillis(); if (range.hasLowerBound() && range.lowerBoundType() == BoundType.OPEN) { @@ -108,7 +104,7 @@ public class DruidDateTimeUtils { return intervals; } - protected static List<Range<Calendar>> extractRanges(RexNode node, + protected static List<Range<TimestampString>> extractRanges(RexNode node, boolean withNot) { switch (node.getKind()) { case EQUALS: @@ -125,9 +121,9 @@ public class DruidDateTimeUtils { case OR: { RexCall call = (RexCall) node; - List<Range<Calendar>> intervals = Lists.newArrayList(); + List<Range<TimestampString>> intervals = Lists.newArrayList(); for (RexNode child : call.getOperands()) { - List<Range<Calendar>> extracted = extractRanges(child, withNot); + List<Range<TimestampString>> extracted = extractRanges(child, withNot); if (extracted != null) { intervals.addAll(extracted); } @@ -137,9 +133,9 @@ public class DruidDateTimeUtils { case AND: { RexCall call = (RexCall) node; - List<Range<Calendar>> ranges = new ArrayList<>(); + List<Range<TimestampString>> ranges = new ArrayList<>(); for (RexNode child : call.getOperands()) { - List<Range<Calendar>> extractedRanges = extractRanges(child, false); + List<Range<TimestampString>> extractedRanges = extractRanges(child, false); if (extractedRanges == null || extractedRanges.isEmpty()) { // We could not extract, we bail out return null; @@ -148,7 +144,7 @@ public class DruidDateTimeUtils { ranges.addAll(extractedRanges); continue; } - List<Range<Calendar>> overlapped = new ArrayList<>(); + List<Range<TimestampString>> overlapped = new ArrayList<>(); for (Range current : ranges) { for (Range interval : extractedRanges) { if (current.isConnected(interval)) { @@ -166,7 +162,7 @@ public class DruidDateTimeUtils { } } - protected static List<Range<Calendar>> leafToRanges(RexCall call, + protected static List<Range<TimestampString>> leafToRanges(RexCall call, boolean withNot) { switch (call.getKind()) { case EQUALS: @@ -175,7 +171,7 @@ public class DruidDateTimeUtils { case GREATER_THAN: case GREATER_THAN_OR_EQUAL: { - final Calendar value; + final TimestampString value; if (call.getOperands().get(0) instanceof RexInputRef && literalValue(call.getOperands().get(1)) != null) { value = literalValue(call.getOperands().get(1)); @@ -203,8 +199,8 @@ public class DruidDateTimeUtils { } case BETWEEN: { - final Calendar value1; - final Calendar value2; + final TimestampString value1; + final TimestampString value2; if (literalValue(call.getOperands().get(2)) != null && literalValue(call.getOperands().get(3)) != null) { value1 = literalValue(call.getOperands().get(2)); @@ -223,9 +219,9 @@ public class DruidDateTimeUtils { } case IN: { - ImmutableList.Builder<Range<Calendar>> ranges = ImmutableList.builder(); + ImmutableList.Builder<Range<TimestampString>> ranges = ImmutableList.builder(); for (RexNode operand : Util.skip(call.operands)) { - final Calendar element = literalValue(operand); + final TimestampString element = literalValue(operand); if (element == null) { return null; } @@ -243,13 +239,16 @@ public class DruidDateTimeUtils { } } - private static Calendar literalValue(RexNode node) { + private static TimestampString literalValue(RexNode node) { switch (node.getKind()) { case LITERAL: - assert node instanceof RexLiteral; - Object value = ((RexLiteral) node).getValue(); - if (value instanceof Calendar) { - return (Calendar) value; + switch (((RexLiteral) node).getTypeName()) { + case TIMESTAMP: + return ((RexLiteral) node).getValueAs(TimestampString.class); + case DATE: + // For uniformity, treat dates as timestamps + final DateString d = ((RexLiteral) node).getValueAs(DateString.class); + return TimestampString.fromMillisSinceEpoch(d.getMillisSinceEpoch()); } break; case CAST: http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java ---------------------------------------------------------------------- diff --git a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java index b2cf321..74ce10c 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java @@ -23,6 +23,7 @@ import org.apache.calcite.rel.rules.DateRangeRules; import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.test.RexImplicationCheckerTest.Fixture; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.collect.ImmutableList; @@ -123,12 +124,13 @@ public class DruidDateRangeRulesTest { * Push CAST of literals to Druid</a>. */ @Test public void testFilterWithCast() { final Fixture2 f = new Fixture2(); - Calendar from = Util.calendar(); - from.clear(); - from.set(2010, Calendar.JANUARY, 1); - Calendar to = Util.calendar(); - to.clear(); - to.set(2011, Calendar.JANUARY, 1); + final Calendar c = Util.calendar(); + c.clear(); + c.set(2010, Calendar.JANUARY, 1); + final TimestampString from = TimestampString.fromCalendarFields(c); + c.clear(); + c.set(2011, Calendar.JANUARY, 1); + final TimestampString to = TimestampString.fromCalendarFields(c); // dt >= 2010-01-01 AND dt < 2011-01-01 checkDateRangeNoSimplify(f,
