From 1d99f7beb2841cb1c74f3a42551e5946905a033f Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 15 Nov 2018 16:25:08 +0200 Subject: [PATCH 1/3] SQL: Move internals from Joda to java.time Try to remove usage of Joda through-out the processors and functions Use ZonedDateTime wherever possible instead of long/tzId Fix #35633 --- .../xpack/sql/action/SqlQueryResponse.java | 16 +++-- .../extractor/CompositeKeyExtractor.java | 37 ++++------ .../search/extractor/FieldHitExtractor.java | 16 +++-- .../scalar/datetime/BaseDateTimeFunction.java | 11 +-- .../datetime/BaseDateTimeProcessor.java | 30 ++++---- .../scalar/datetime/DateTimeFunction.java | 32 ++++----- .../datetime/DateTimeHistogramFunction.java | 5 +- .../scalar/datetime/DateTimeProcessor.java | 48 ++++++------- .../function/scalar/datetime/DayOfMonth.java | 13 +--- .../function/scalar/datetime/DayOfWeek.java | 13 +--- .../function/scalar/datetime/DayOfYear.java | 13 +--- .../function/scalar/datetime/HourOfDay.java | 13 +--- .../function/scalar/datetime/MinuteOfDay.java | 13 +--- .../scalar/datetime/MinuteOfHour.java | 13 +--- .../function/scalar/datetime/MonthOfYear.java | 13 +--- .../datetime/NamedDateTimeFunction.java | 5 +- .../datetime/NamedDateTimeProcessor.java | 9 ++- .../function/scalar/datetime/Quarter.java | 5 +- .../scalar/datetime/QuarterProcessor.java | 14 ++-- .../scalar/datetime/SecondOfMinute.java | 13 +--- .../function/scalar/datetime/WeekOfYear.java | 13 +--- .../function/scalar/datetime/Year.java | 13 +--- .../operator/arithmetic/Arithmetics.java | 68 +++++++++++++++++++ .../xpack/sql/parser/ExpressionBuilder.java | 7 +- .../xpack/sql/type/DataTypeConversion.java | 17 ++--- .../xpack/sql/type/DataTypes.java | 6 +- .../xpack/sql/util/DateUtils.java | 60 ++++++++++++++++ .../extractor/CompositeKeyExtractorTests.java | 5 +- .../extractor/FieldHitExtractorTests.java | 5 +- .../datetime/DateTimeProcessorTests.java | 14 ++-- .../scalar/datetime/DateTimeTestUtils.java | 31 +++++++++ .../scalar/datetime/DayOfYearTests.java | 8 +-- .../datetime/NamedDateTimeProcessorTests.java | 68 +++++++++---------- .../datetime/QuarterProcessorTests.java | 34 +++++----- .../sql/planner/QueryTranslatorTests.java | 4 +- .../sql/type/DataTypeConversionTests.java | 30 ++++---- 36 files changed, 377 insertions(+), 338 deletions(-) create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java create mode 100644 x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeTestUtils.java diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index da4037ac95c64..bfc76d1395d34 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -14,10 +14,10 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.sql.proto.ColumnInfo; import org.elasticsearch.xpack.sql.proto.Mode; -import org.joda.time.ReadableDateTime; import java.io.IOException; import java.sql.JDBCType; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -167,9 +167,17 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws * Serializes the provided value in SQL-compatible way based on the client mode */ public static XContentBuilder value(XContentBuilder builder, Mode mode, Object value) throws IOException { - if (Mode.isDriver(mode) && value instanceof ReadableDateTime) { - // JDBC cannot parse dates in string format - builder.value(((ReadableDateTime) value).getMillis()); + if (value instanceof ZonedDateTime) { + ZonedDateTime zdt = (ZonedDateTime) value; + if (Mode.isDriver(mode)) { + // JDBC cannot parse dates in string format and ODBC can have issues with it + // so instead, use the millis since epoch (in UTC) + builder.value(zdt.toInstant().toEpochMilli()); + } + // otherwise use the ISO format + else { + builder.value(zdt.toLocalDateTime().toString() + zdt.getOffset().toString()); + } } else { builder.value(value); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java index e8e2db4f052b2..c799ab27dcab2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java @@ -5,16 +5,15 @@ */ package org.elasticsearch.xpack.sql.execution.search.extractor; -import org.elasticsearch.Version; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.querydsl.container.GroupByRef.Property; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.util.DateUtils; import java.io.IOException; +import java.time.ZoneId; import java.util.Map; import java.util.Objects; import java.util.TimeZone; @@ -29,6 +28,7 @@ public class CompositeKeyExtractor implements BucketExtractor { private final String key; private final Property property; private final TimeZone timeZone; + private final ZoneId zoneId; /** * Constructs a new CompositeKeyExtractor instance. @@ -38,40 +38,29 @@ public CompositeKeyExtractor(String key, Property property, TimeZone timeZone) { this.key = key; this.property = property; this.timeZone = timeZone; + this.zoneId = timeZone != null ? timeZone.toZoneId() : null; } CompositeKeyExtractor(StreamInput in) throws IOException { key = in.readString(); property = in.readEnum(Property.class); - if (in.getVersion().onOrAfter(Version.V_6_3_0)) { - if (in.readBoolean()) { - timeZone = TimeZone.getTimeZone(in.readString()); - } else { - timeZone = null; - } + if (in.readBoolean()) { + timeZone = TimeZone.getTimeZone(in.readString()); } else { - DateTimeZone dtz = in.readOptionalTimeZone(); - if (dtz == null) { - timeZone = null; - } else { - timeZone = dtz.toTimeZone(); - } + timeZone = null; } + this.zoneId = timeZone != null ? timeZone.toZoneId() : null; } @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(key); out.writeEnum(property); - if (out.getVersion().onOrAfter(Version.V_6_3_0)) { - if (timeZone == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - out.writeString(timeZone.getID()); - } + if (timeZone == null) { + out.writeBoolean(false); } else { - out.writeOptionalTimeZone(timeZone == null ? null : DateTimeZone.forTimeZone(timeZone)); + out.writeBoolean(true); + out.writeString(timeZone.getID()); } } @@ -110,7 +99,7 @@ public Object extract(Bucket bucket) { if (object == null) { return object; } else if (object instanceof Long) { - object = new DateTime(((Long) object).longValue(), DateTimeZone.forTimeZone(timeZone)); + object = DateUtils.of(((Long) object).longValue(), zoneId); } else { throw new SqlIllegalArgumentException("Invalid date key returned: {}", object); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java index 66e177530547f..3284efa54c8f4 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java @@ -13,9 +13,8 @@ import org.elasticsearch.search.SearchHit; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.util.DateUtils; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; import java.io.IOException; import java.util.List; @@ -136,11 +135,16 @@ private Object unwrapMultiValue(Object values) { if (values instanceof Map) { throw new SqlIllegalArgumentException("Objects (returned by [{}]) are not supported", fieldName); } - if (values instanceof String && dataType == DataType.DATE) { - return new DateTime(Long.parseLong(values.toString()), DateTimeZone.UTC); + if (dataType == DataType.DATE) { + if (values instanceof String) { + return DateUtils.of(Long.parseLong(values.toString())); + } + // returned by nested types... + if (values instanceof DateTime) { + return DateUtils.of((DateTime) values); + } } - if (values instanceof Long || values instanceof Double || values instanceof String || values instanceof Boolean - || values instanceof ReadableDateTime) { + if (values instanceof Long || values instanceof Double || values instanceof String || values instanceof Boolean) { return values; } throw new SqlIllegalArgumentException("Type {} (returned by [{}]) is not supported", values.getClass().getSimpleName(), fieldName); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeFunction.java index 130acd8eddcd3..cfee964b01e62 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeFunction.java @@ -12,19 +12,22 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; -import org.joda.time.DateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Objects; import java.util.TimeZone; abstract class BaseDateTimeFunction extends UnaryScalarFunction { private final TimeZone timeZone; + private final ZoneId zoneId; private final String name; BaseDateTimeFunction(Location location, Expression field, TimeZone timeZone) { super(location, field); this.timeZone = timeZone; + this.zoneId = timeZone != null ? timeZone.toZoneId() : null; StringBuilder sb = new StringBuilder(super.name()); // add timezone as last argument @@ -61,15 +64,15 @@ public boolean foldable() { @Override public Object fold() { - DateTime folded = (DateTime) field().fold(); + ZonedDateTime folded = (ZonedDateTime) field().fold(); if (folded == null) { return null; } - return doFold(folded.getMillis(), timeZone().getID()); + return doFold(folded.withZoneSameInstant(zoneId)); } - protected abstract Object doFold(long millis, String tzId); + protected abstract Object doFold(ZonedDateTime dateTime); @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeProcessor.java index c8f42704ac525..ce6bd1ad470aa 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/BaseDateTimeProcessor.java @@ -10,21 +10,25 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; -import org.joda.time.ReadableInstant; import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.TimeZone; public abstract class BaseDateTimeProcessor implements Processor { private final TimeZone timeZone; + private final ZoneId zoneId; BaseDateTimeProcessor(TimeZone timeZone) { this.timeZone = timeZone; + this.zoneId = timeZone.toZoneId(); } BaseDateTimeProcessor(StreamInput in) throws IOException { timeZone = TimeZone.getTimeZone(in.readString()); + zoneId = timeZone.toZoneId(); } @Override @@ -37,23 +41,17 @@ TimeZone timeZone() { } @Override - public Object process(Object l) { - if (l == null) { + public Object process(Object input) { + if (input == null) { return null; } - long millis; - if (l instanceof String) { - // 6.4+ - millis = Long.parseLong(l.toString()); - } else if (l instanceof ReadableInstant) { - // 6.3- - millis = ((ReadableInstant) l).getMillis(); - } else { - throw new SqlIllegalArgumentException("A string or a date is required; received {}", l); + + if (!(input instanceof ZonedDateTime)) { + throw new SqlIllegalArgumentException("A date is required; received {}", input); } - - return doProcess(millis); + + return doProcess(((ZonedDateTime) input).withZoneSameInstant(zoneId)); } - abstract Object doProcess(long millis); -} + abstract Object doProcess(ZonedDateTime dateTime); +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java index 8d5a384b1f456..cad8265a03d65 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; -import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; @@ -24,23 +23,25 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction { - DateTimeFunction(Location location, Expression field, TimeZone timeZone) { + private final DateTimeExtractor extractor; + + DateTimeFunction(Location location, Expression field, TimeZone timeZone, DateTimeExtractor extractor) { super(location, field, timeZone); + this.extractor = extractor; } @Override - protected Object doFold(long millis, String tzId) { - return dateTimeChrono(millis, tzId, chronoField().name()); + protected Object doFold(ZonedDateTime dateTime) { + return dateTimeChrono(dateTime, extractor.chronoField()); } - public static Integer dateTimeChrono(long millis, String tzId, String chronoName) { - ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)); - return Integer.valueOf(time.get(ChronoField.valueOf(chronoName))); + public static Integer dateTimeChrono(ZonedDateTime dateTime, String tzId, String chronoName) { + ZonedDateTime zdt = dateTime.withZoneSameInstant(ZoneId.of(tzId)); + return dateTimeChrono(zdt, ChronoField.valueOf(chronoName)); } - public static Integer dateTimeChrono(ZonedDateTime millis, String tzId, String chronoName) { - ZonedDateTime time = millis.withZoneSameInstant(ZoneId.of(tzId)); - return Integer.valueOf(time.get(ChronoField.valueOf(chronoName))); + private static Integer dateTimeChrono(ZonedDateTime dateTime, ChronoField field) { + return Integer.valueOf(dateTime.get(field)); } @Override @@ -51,21 +52,14 @@ public ScriptTemplate scriptWithField(FieldAttribute field) { template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value, {}, {})"); params.variable(field.name()) .variable(timeZone().getID()) - .variable(chronoField().name()); + .variable(extractor.chronoField().name()); return new ScriptTemplate(template, params.build(), dataType()); } - /** - * Used for generating the painless script version of this function when the time zone is not UTC - */ - protected abstract ChronoField chronoField(); - - protected abstract DateTimeExtractor extractor(); - @Override protected Processor makeProcessor() { - return new DateTimeProcessor(extractor(), timeZone()); + return new DateTimeProcessor(extractor, timeZone()); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeHistogramFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeHistogramFunction.java index bb5aaea61fb3f..60d39e7ea30bc 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeHistogramFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeHistogramFunction.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; import java.util.TimeZone; @@ -16,8 +17,8 @@ */ public abstract class DateTimeHistogramFunction extends DateTimeFunction { - DateTimeHistogramFunction(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + DateTimeHistogramFunction(Location location, Expression field, TimeZone timeZone, DateTimeExtractor extractor) { + super(location, field, timeZone, extractor); } /** diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessor.java index d34b1c1e39053..d1a19a5ba014a 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessor.java @@ -7,38 +7,40 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.joda.time.DateTime; -import org.joda.time.DateTimeFieldType; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; import java.io.IOException; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; import java.util.Objects; import java.util.TimeZone; public class DateTimeProcessor extends BaseDateTimeProcessor { public enum DateTimeExtractor { - DAY_OF_MONTH(DateTimeFieldType.dayOfMonth()), - DAY_OF_WEEK(DateTimeFieldType.dayOfWeek()), - DAY_OF_YEAR(DateTimeFieldType.dayOfYear()), - HOUR_OF_DAY(DateTimeFieldType.hourOfDay()), - MINUTE_OF_DAY(DateTimeFieldType.minuteOfDay()), - MINUTE_OF_HOUR(DateTimeFieldType.minuteOfHour()), - MONTH_OF_YEAR(DateTimeFieldType.monthOfYear()), - SECOND_OF_MINUTE(DateTimeFieldType.secondOfMinute()), - WEEK_OF_YEAR(DateTimeFieldType.weekOfWeekyear()), - YEAR(DateTimeFieldType.year()); - - private final DateTimeFieldType field; - - DateTimeExtractor(DateTimeFieldType field) { + DAY_OF_MONTH(ChronoField.DAY_OF_MONTH), + DAY_OF_WEEK(ChronoField.DAY_OF_WEEK), + DAY_OF_YEAR(ChronoField.DAY_OF_YEAR), + HOUR_OF_DAY(ChronoField.HOUR_OF_DAY), + MINUTE_OF_DAY(ChronoField.MINUTE_OF_DAY), + MINUTE_OF_HOUR(ChronoField.MINUTE_OF_HOUR), + MONTH_OF_YEAR(ChronoField.MONTH_OF_YEAR), + SECOND_OF_MINUTE(ChronoField.SECOND_OF_MINUTE), + WEEK_OF_YEAR(ChronoField.ALIGNED_WEEK_OF_YEAR), + YEAR(ChronoField.YEAR); + + private final ChronoField field; + + DateTimeExtractor(ChronoField field) { this.field = field; } - public int extract(ReadableDateTime dt) { + public int extract(ZonedDateTime dt) { return dt.get(field); } + + public ChronoField chronoField() { + return field; + } } public static final String NAME = "dt"; @@ -70,10 +72,8 @@ DateTimeExtractor extractor() { } @Override - public Object doProcess(long millis) { - ReadableDateTime dt = new DateTime(millis, DateTimeZone.forTimeZone(timeZone())); - - return extractor.extract(dt); + public Object doProcess(ZonedDateTime dateTime) { + return extractor.extract(dateTime); } @Override @@ -95,4 +95,4 @@ public boolean equals(Object obj) { public String toString() { return extractor.toString(); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java index ebb576b4648e1..3c402ef2f4a8d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class DayOfMonth extends DateTimeFunction { public DayOfMonth(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.DAY_OF_MONTH); } @Override @@ -35,14 +34,4 @@ protected DayOfMonth replaceChild(Expression newChild) { public String dateTimeFormat() { return "d"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.DAY_OF_MONTH; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.DAY_OF_MONTH; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java index d840d4d71df0a..fbfd9c9861768 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class DayOfWeek extends DateTimeFunction { public DayOfWeek(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.DAY_OF_WEEK); } @Override @@ -35,14 +34,4 @@ protected DayOfWeek replaceChild(Expression newChild) { public String dateTimeFormat() { return "e"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.DAY_OF_WEEK; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.DAY_OF_WEEK; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java index 1fa248d9c2063..a6b843bd0bd04 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -19,7 +18,7 @@ */ public class DayOfYear extends DateTimeFunction { public DayOfYear(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.DAY_OF_YEAR); } @Override @@ -36,14 +35,4 @@ protected UnaryScalarFunction replaceChild(Expression newChild) { public String dateTimeFormat() { return "D"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.DAY_OF_YEAR; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.DAY_OF_YEAR; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java index 4df28bddad088..193a14c09327d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class HourOfDay extends DateTimeFunction { public HourOfDay(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.HOUR_OF_DAY); } @Override @@ -35,14 +34,4 @@ protected HourOfDay replaceChild(Expression newChild) { public String dateTimeFormat() { return "hour"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.HOUR_OF_DAY; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.HOUR_OF_DAY; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java index ef0fb0bce18aa..25ef41a18cac8 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -19,7 +18,7 @@ public class MinuteOfDay extends DateTimeFunction { public MinuteOfDay(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.MINUTE_OF_DAY); } @Override @@ -36,14 +35,4 @@ protected MinuteOfDay replaceChild(Expression newChild) { public String dateTimeFormat() { throw new UnsupportedOperationException("is there a format for it?"); } - - @Override - protected ChronoField chronoField() { - return ChronoField.MINUTE_OF_DAY; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.MINUTE_OF_DAY; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java index f5ab095ef2455..798b700723724 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class MinuteOfHour extends DateTimeFunction { public MinuteOfHour(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.MINUTE_OF_HOUR); } @Override @@ -35,14 +34,4 @@ protected MinuteOfHour replaceChild(Expression newChild) { public String dateTimeFormat() { return "m"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.MINUTE_OF_HOUR; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.MINUTE_OF_HOUR; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java index 503a771611e7d..9231987b5add2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class MonthOfYear extends DateTimeFunction { public MonthOfYear(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.MONTH_OF_YEAR); } @Override @@ -35,14 +34,4 @@ protected MonthOfYear replaceChild(Expression newChild) { public String dateTimeFormat() { return "M"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.MONTH_OF_YEAR; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.MONTH_OF_YEAR; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeFunction.java index a8e6e02057a22..4ec42def0ebf3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeFunction.java @@ -14,6 +14,7 @@ import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.StringUtils; +import java.time.ZonedDateTime; import java.util.Locale; import java.util.TimeZone; @@ -33,8 +34,8 @@ abstract class NamedDateTimeFunction extends BaseDateTimeFunction { } @Override - protected Object doFold(long millis, String tzId) { - return nameExtractor.extract(millis, tzId); + protected Object doFold(ZonedDateTime dateTime) { + return nameExtractor.extract(dateTime); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessor.java index 50eac88ae2c44..a0707d2a65e5f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessor.java @@ -9,7 +9,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import java.io.IOException; -import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -31,8 +30,8 @@ public enum NameExtractor { this.apply = apply; } - public final String extract(Long millis, String tzId) { - return extract(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)), tzId); + public final String extract(ZonedDateTime dateTime) { + return apply.apply(dateTime); } public final String extract(ZonedDateTime millis, String tzId) { @@ -73,8 +72,8 @@ NameExtractor extractor() { } @Override - public Object doProcess(long millis) { - return extractor.extract(millis, timeZone().getID()); + public Object doProcess(ZonedDateTime dateTime) { + return extractor.extract(dateTime); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Quarter.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Quarter.java index 51b9501c6eb00..4da5c94626e27 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Quarter.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Quarter.java @@ -14,6 +14,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.elasticsearch.xpack.sql.type.DataType; +import java.time.ZonedDateTime; import java.util.TimeZone; import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor.quarter; @@ -26,8 +27,8 @@ public Quarter(Location location, Expression field, TimeZone timeZone) { } @Override - protected Object doFold(long millis, String tzId) { - return quarter(millis, tzId); + protected Object doFold(ZonedDateTime dateTime) { + return quarter(dateTime); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessor.java index c4d6864b27574..d2a20de84d303 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessor.java @@ -9,7 +9,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import java.io.IOException; -import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -36,17 +35,16 @@ public String getWriteableName() { } @Override - public Object doProcess(long millis) { - return quarter(millis, timeZone().getID()); + public Object doProcess(ZonedDateTime zdt) { + return quarter(zdt); } - public static Integer quarter(long millis, String tzId) { - return quarter(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)), tzId); + public static Integer quarter(ZonedDateTime dateTime, String tzId) { + return quarter(dateTime.withZoneSameInstant(ZoneId.of(tzId))); } - public static Integer quarter(ZonedDateTime zdt, String tzId) { - ZonedDateTime time = zdt.withZoneSameInstant(ZoneId.of(tzId)); - return Integer.valueOf(time.format(QUARTER_FORMAT)); + static Integer quarter(ZonedDateTime zdt) { + return Integer.valueOf(zdt.format(QUARTER_FORMAT)); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java index 3522eb10ffe80..3702c4beb3f6f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class SecondOfMinute extends DateTimeFunction { public SecondOfMinute(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.SECOND_OF_MINUTE); } @Override @@ -35,14 +34,4 @@ protected SecondOfMinute replaceChild(Expression newChild) { public String dateTimeFormat() { return "s"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.SECOND_OF_MINUTE; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.SECOND_OF_MINUTE; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfYear.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfYear.java index 59948165f71cb..8a31ffe36eec8 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfYear.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfYear.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class WeekOfYear extends DateTimeFunction { public WeekOfYear(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.WEEK_OF_YEAR); } @Override @@ -35,14 +34,4 @@ protected WeekOfYear replaceChild(Expression newChild) { public String dateTimeFormat() { return "w"; } - - @Override - protected ChronoField chronoField() { - return ChronoField.ALIGNED_WEEK_OF_YEAR; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.WEEK_OF_YEAR; - } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java index 2b065329be305..2eb08c7dd93b8 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import java.time.temporal.ChronoField; import java.util.TimeZone; /** @@ -18,7 +17,7 @@ */ public class Year extends DateTimeHistogramFunction { public Year(Location location, Expression field, TimeZone timeZone) { - super(location, field, timeZone); + super(location, field, timeZone, DateTimeExtractor.YEAR); } @Override @@ -41,16 +40,6 @@ public Expression orderBy() { return field(); } - @Override - protected ChronoField chronoField() { - return ChronoField.YEAR; - } - - @Override - protected DateTimeExtractor extractor() { - return DateTimeExtractor.YEAR; - } - @Override public String interval() { return "year"; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java index 07fcef391681e..bec35eb449ca9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java @@ -5,6 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic; +import java.time.Duration; +import java.time.Period; +import java.time.ZonedDateTime; + /** * Arithmetic operation using the type widening rules of the JLS 5.6.2 namely * widen to double or float or long or int in this order. @@ -29,6 +33,38 @@ static Number add(Number l, Number r) { return Integer.valueOf(Math.addExact(l.intValue(), r.intValue())); } + static Period add(Period l, Period r) { + if (l == null || r == null) { + return null; + } + + return l.plus(r); + } + + static Duration add(Duration l, Duration r) { + if (l == null || r == null) { + return null; + } + + return l.plus(r); + } + + static ZonedDateTime add(ZonedDateTime l, Period r) { + if (l == null || r == null) { + return null; + } + + return l.plus(r); + } + + static ZonedDateTime add(ZonedDateTime l, Duration r) { + if (l == null || r == null) { + return null; + } + + return l.plus(r); + } + static Number sub(Number l, Number r) { if (l == null || r == null) { return null; @@ -47,6 +83,38 @@ static Number sub(Number l, Number r) { return Integer.valueOf(Math.subtractExact(l.intValue(), r.intValue())); } + static Period sub(Period l, Period r) { + if (l == null || r == null) { + return null; + } + + return l.minus(r); + } + + static Duration sub(Duration l, Duration r) { + if (l == null || r == null) { + return null; + } + + return l.minus(r); + } + + static ZonedDateTime sub(ZonedDateTime l, Period r) { + if (l == null || r == null) { + return null; + } + + return l.minus(r); + } + + static ZonedDateTime sub(ZonedDateTime l, Duration r) { + if (l == null || r == null) { + return null; + } + + return l.minus(r); + } + static Number mul(Number l, Number r) { if (l == null || r == null) { return null; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java index fc3d023c9b492..b2e8e0c02b0ef 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java @@ -24,7 +24,6 @@ import org.elasticsearch.xpack.sql.expression.function.Function; import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.Cast; -import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull; import org.elasticsearch.xpack.sql.expression.predicate.Range; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate; @@ -33,6 +32,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.logical.Not; import org.elasticsearch.xpack.sql.expression.predicate.logical.Or; import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNull; +import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod; @@ -94,6 +94,7 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import org.elasticsearch.xpack.sql.util.DateUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; @@ -631,7 +632,7 @@ public Literal visitDateEscapedLiteral(DateEscapedLiteralContext ctx) { } catch(IllegalArgumentException ex) { throw new ParsingException(loc, "Invalid date received; {}", ex.getMessage()); } - return new Literal(loc, dt, DataType.DATE); + return new Literal(loc, DateUtils.of(dt), DataType.DATE); } @Override @@ -667,7 +668,7 @@ public Literal visitTimestampEscapedLiteral(TimestampEscapedLiteralContext ctx) } catch (IllegalArgumentException ex) { throw new ParsingException(loc, "Invalid timestamp received; {}", ex.getMessage()); } - return new Literal(loc, dt, DataType.DATE); + return new Literal(loc, DateUtils.of(dt), DataType.DATE); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 26436c614f565..8c21edcee349d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -8,12 +8,9 @@ import org.elasticsearch.common.Booleans; import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableInstant; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; +import org.elasticsearch.xpack.sql.util.DateUtils; +import java.time.ZonedDateTime; import java.util.Locale; import java.util.function.DoubleFunction; import java.util.function.Function; @@ -32,8 +29,6 @@ */ public abstract class DataTypeConversion { - private static final DateTimeFormatter UTC_DATE_FORMATTER = ISODateTimeFormat.dateOptionalTimeParser().withZoneUTC(); - /** * Returns the type compatible with both left and right types *

@@ -374,7 +369,7 @@ public enum Conversion { IDENTITY(Function.identity()), NULL(value -> null), - DATE_TO_STRING(Object::toString), + DATE_TO_STRING(o -> DateUtils.toString((ZonedDateTime) o)), OTHER_TO_STRING(String::valueOf), RATIONAL_TO_LONG(fromDouble(DataTypeConversion::safeToLong)), @@ -416,7 +411,7 @@ public enum Conversion { RATIONAL_TO_DATE(toDate(RATIONAL_TO_LONG)), INTEGER_TO_DATE(toDate(INTEGER_TO_LONG)), BOOL_TO_DATE(toDate(BOOL_TO_INT)), - STRING_TO_DATE(fromString(UTC_DATE_FORMATTER::parseDateTime, "Date")), + STRING_TO_DATE(fromString(DateUtils::of, "Date")), NUMERIC_TO_BOOLEAN(fromLong(value -> value != 0)), STRING_TO_BOOLEAN(fromString(DataTypeConversion::convertToBoolean, "Boolean")), @@ -462,11 +457,11 @@ private static Function fromBool(Function conve } private static Function fromDate(Function converter) { - return l -> ((ReadableInstant) l).getMillis(); + return l -> ((ZonedDateTime) l).toEpochSecond(); } private static Function toDate(Conversion conversion) { - return l -> new DateTime(((Number) conversion.convert(l)).longValue(), DateTimeZone.UTC); + return l -> DateUtils.of(((Number) conversion.convert(l)).longValue()); } public Object convert(Object l) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index 92bc6f33a5de5..91de6297b9449 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -6,7 +6,9 @@ package org.elasticsearch.xpack.sql.type; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; -import org.joda.time.DateTime; + +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; public final class DataTypes { @@ -45,7 +47,7 @@ public static DataType fromJava(Object value) { if (value instanceof Short) { return DataType.SHORT; } - if (value instanceof DateTime) { + if (value instanceof ZonedDateTime || value instanceof OffsetDateTime) { return DataType.DATE; } if (value instanceof String || value instanceof Character) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java new file mode 100644 index 0000000000000..06ace15709606 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.util; + +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +public class DateUtils { + + // TODO: do we have a java.time based parser we can use instead? + private static final DateTimeFormatter UTC_DATE_FORMATTER = ISODateTimeFormat.dateOptionalTimeParser().withZoneUTC(); + + public static ZoneId UTC = ZoneId.of("UTC"); + + private DateUtils() {} + + + public static ZonedDateTime of(long millis) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC); + } + + public static ZonedDateTime of(long millis, ZoneId id) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), id); + } + + public static ZonedDateTime of(String dateFormat) { + return of(UTC_DATE_FORMATTER.parseDateTime(dateFormat)); + } + + public static ZonedDateTime of(DateTime dateTime) { + LocalDateTime ldt = LocalDateTime.of( + dateTime.getYear(), + dateTime.getMonthOfYear(), + dateTime.getDayOfMonth(), + dateTime.getHourOfDay(), + dateTime.getMinuteOfHour(), + dateTime.getSecondOfMinute(), + dateTime.getMillisOfSecond() * 1_000_000); + + return ZonedDateTime.ofStrict(ldt, + ZoneOffset.ofTotalSeconds(dateTime.getZone().getOffset(dateTime) / 1000), + dateTime.getZone().toTimeZone().toZoneId()); + } + + public static String toString(ZonedDateTime dateTime) { + // alternative to toOffsetDateTime().toString() - avoids creating the intermediate OffsetDateTime + return dateTime.toLocalDateTime().toString() + dateTime.getOffset().toString(); + } +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java index 11068372bcc8a..c0125a365aac8 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java @@ -11,8 +11,7 @@ import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.querydsl.container.GroupByRef.Property; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.util.DateUtils; import java.io.IOException; import java.util.TimeZone; @@ -63,7 +62,7 @@ public void testExtractDate() { long millis = System.currentTimeMillis(); Bucket bucket = new TestBucket(singletonMap(extractor.key(), millis), randomLong(), new Aggregations(emptyList())); - assertEquals(new DateTime(millis, DateTimeZone.forTimeZone(extractor.timeZone())), extractor.extract(bucket)); + assertEquals(DateUtils.of(millis, extractor.timeZone().toZoneId()), extractor.extract(bucket)); } public void testExtractIncorrectDateKey() { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java index 9aa0c9f7b36c2..5c3478eaea343 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java @@ -15,8 +15,7 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlException; import org.elasticsearch.xpack.sql.type.DataType; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.util.DateUtils; import java.io.IOException; import java.util.ArrayList; @@ -144,7 +143,7 @@ public void testGetDate() { DocumentField field = new DocumentField("my_date_field", documentFieldValues); hit.fields(singletonMap("my_date_field", field)); FieldHitExtractor extractor = new FieldHitExtractor("my_date_field", DataType.DATE, true); - assertEquals(new DateTime(millis, DateTimeZone.UTC), extractor.extract(hit)); + assertEquals(DateUtils.of(millis), extractor.extract(hit)); } public void testGetSource() throws IOException { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessorTests.java index 8b0af5e968137..30c5fa6cb4e5a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeProcessorTests.java @@ -8,12 +8,12 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.io.IOException; import java.util.TimeZone; +import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; + public class DateTimeProcessorTests extends AbstractWireSerializingTestCase { private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); @@ -39,12 +39,12 @@ protected DateTimeProcessor mutateInstance(DateTimeProcessor instance) throws IO public void testApply() { DateTimeProcessor proc = new DateTimeProcessor(DateTimeExtractor.YEAR, UTC); - assertEquals(1970, proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals(2017, proc.process(new DateTime(2017, 01, 02, 10, 10, DateTimeZone.UTC))); + assertEquals(1970, proc.process(dateTime(0L))); + assertEquals(2017, proc.process(dateTime(2017, 01, 02, 10, 10))); proc = new DateTimeProcessor(DateTimeExtractor.DAY_OF_MONTH, UTC); - assertEquals(1, proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals(2, proc.process(new DateTime(2017, 01, 02, 10, 10, DateTimeZone.UTC))); - assertEquals(31, proc.process(new DateTime(2017, 01, 31, 10, 10, DateTimeZone.UTC))); + assertEquals(1, proc.process(dateTime(0L))); + assertEquals(2, proc.process(dateTime(2017, 01, 02, 10, 10))); + assertEquals(31, proc.process(dateTime(2017, 01, 31, 10, 10))); } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeTestUtils.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeTestUtils.java new file mode 100644 index 0000000000000..164fe1fe931a4 --- /dev/null +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeTestUtils.java @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; + +import org.elasticsearch.xpack.sql.util.DateUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.time.ZonedDateTime; + +import static org.junit.Assert.assertEquals; + +public class DateTimeTestUtils { + + private DateTimeTestUtils() {} + + public static ZonedDateTime dateTime(int year, int month, int day, int hour, int minute) { + DateTime dateTime = new DateTime(year, month, day, hour, minute, DateTimeZone.UTC); + ZonedDateTime zdt = ZonedDateTime.of(year, month, day, hour, minute, 0, 0, DateUtils.UTC); + assertEquals(dateTime.getMillis() / 1000, zdt.toEpochSecond()); + return zdt; + } + + public static ZonedDateTime dateTime(long millisSinceEpoch) { + return DateUtils.of(millisSinceEpoch); + } +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYearTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYearTests.java index 0bd54bd738239..c134446a2c340 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYearTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYearTests.java @@ -8,11 +8,11 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.type.DataType; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.util.TimeZone; +import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; + public class DayOfYearTests extends ESTestCase { private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); @@ -22,10 +22,6 @@ public void testAsColumnProcessor() { assertEquals(365, extract(dateTime(0), TimeZone.getTimeZone("GMT-01:00"))); } - private DateTime dateTime(long millisSinceEpoch) { - return new DateTime(millisSinceEpoch, DateTimeZone.forTimeZone(UTC)); - } - private Object extract(Object value, TimeZone timeZone) { return build(value, timeZone).asPipe().asProcessor().process(value); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessorTests.java index 0f12ae05f86d4..379cf5f7e090f 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/NamedDateTimeProcessorTests.java @@ -10,13 +10,13 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.junit.Assume; import java.io.IOException; import java.util.TimeZone; +import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; + public class NamedDateTimeProcessorTests extends AbstractWireSerializingTestCase { private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); @@ -44,55 +44,55 @@ protected NamedDateTimeProcessor mutateInstance(NamedDateTimeProcessor instance) public void testValidDayNamesInUTC() { assumeJava9PlusAndCompatLocaleProviderSetting(); NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.DAY_NAME, UTC); - assertEquals("Thursday", proc.process("0")); - assertEquals("Saturday", proc.process("-64164233612338")); - assertEquals("Monday", proc.process("64164233612338")); + assertEquals("Thursday", proc.process(dateTime(0L))); + assertEquals("Saturday", proc.process(dateTime(-64164233612338L))); + assertEquals("Monday", proc.process(dateTime(64164233612338L))); - assertEquals("Thursday", proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals("Thursday", proc.process(new DateTime(-5400, 12, 25, 2, 0, DateTimeZone.UTC))); - assertEquals("Friday", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC))); - assertEquals("Tuesday", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC))); + assertEquals("Thursday", proc.process(dateTime(0L))); + assertEquals("Thursday", proc.process(dateTime(-5400, 12, 25, 2, 0))); + assertEquals("Friday", proc.process(dateTime(30, 2, 1, 12, 13))); + assertEquals("Tuesday", proc.process(dateTime(10902, 8, 22, 11, 11))); } public void testValidDayNamesWithNonUTCTimeZone() { assumeJava9PlusAndCompatLocaleProviderSetting(); NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.DAY_NAME, TimeZone.getTimeZone("GMT-10:00")); - assertEquals("Wednesday", proc.process("0")); - assertEquals("Friday", proc.process("-64164233612338")); - assertEquals("Monday", proc.process("64164233612338")); + assertEquals("Wednesday", proc.process(dateTime(0))); + assertEquals("Friday", proc.process(dateTime(-64164233612338L))); + assertEquals("Monday", proc.process(dateTime(64164233612338L))); - assertEquals("Wednesday", proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals("Wednesday", proc.process(new DateTime(-5400, 12, 25, 2, 0, DateTimeZone.UTC))); - assertEquals("Friday", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC))); - assertEquals("Tuesday", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC))); - assertEquals("Monday", proc.process(new DateTime(10902, 8, 22, 9, 59, DateTimeZone.UTC))); + assertEquals("Wednesday", proc.process(dateTime(0L))); + assertEquals("Wednesday", proc.process(dateTime(-5400, 12, 25, 2, 0))); + assertEquals("Friday", proc.process(dateTime(30, 2, 1, 12, 13))); + assertEquals("Tuesday", proc.process(dateTime(10902, 8, 22, 11, 11))); + assertEquals("Monday", proc.process(dateTime(10902, 8, 22, 9, 59))); } public void testValidMonthNamesInUTC() { assumeJava9PlusAndCompatLocaleProviderSetting(); NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.MONTH_NAME, UTC); - assertEquals("January", proc.process("0")); - assertEquals("September", proc.process("-64164233612338")); - assertEquals("April", proc.process("64164233612338")); + assertEquals("January", proc.process(dateTime(0))); + assertEquals("September", proc.process(dateTime(-64165813612338L))); + assertEquals("April", proc.process(dateTime(64164233612338L))); - assertEquals("January", proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals("December", proc.process(new DateTime(-5400, 12, 25, 10, 10, DateTimeZone.UTC))); - assertEquals("February", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC))); - assertEquals("August", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC))); + assertEquals("January", proc.process(dateTime(0L))); + assertEquals("December", proc.process(dateTime(-5400, 12, 25, 10, 10))); + assertEquals("February", proc.process(dateTime(30, 2, 1, 12, 13))); + assertEquals("August", proc.process(dateTime(10902, 8, 22, 11, 11))); } public void testValidMonthNamesWithNonUTCTimeZone() { assumeJava9PlusAndCompatLocaleProviderSetting(); NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.MONTH_NAME, TimeZone.getTimeZone("GMT-3:00")); - assertEquals("December", proc.process("0")); - assertEquals("August", proc.process("-64165813612338")); // GMT: Tuesday, September 1, -0064 2:53:07.662 AM - assertEquals("April", proc.process("64164233612338")); // GMT: Monday, April 14, 4003 2:13:32.338 PM + assertEquals("December", proc.process(dateTime(0))); + assertEquals("August", proc.process(dateTime(-64165813612338L))); // GMT: Tuesday, September 1, -0064 2:53:07.662 AM + assertEquals("April", proc.process(dateTime(64164233612338L))); // GMT: Monday, April 14, 4003 2:13:32.338 PM - assertEquals("December", proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals("November", proc.process(new DateTime(-5400, 12, 1, 1, 1, DateTimeZone.UTC))); - assertEquals("February", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC))); - assertEquals("July", proc.process(new DateTime(10902, 8, 1, 2, 59, DateTimeZone.UTC))); - assertEquals("August", proc.process(new DateTime(10902, 8, 1, 3, 00, DateTimeZone.UTC))); + assertEquals("December", proc.process(dateTime(0L))); + assertEquals("November", proc.process(dateTime(-5400, 12, 1, 1, 1))); + assertEquals("February", proc.process(dateTime(30, 2, 1, 12, 13))); + assertEquals("July", proc.process(dateTime(10902, 8, 1, 2, 59))); + assertEquals("August", proc.process(dateTime(10902, 8, 1, 3, 00))); } /* @@ -109,8 +109,8 @@ private void assumeJava9PlusAndCompatLocaleProviderSetting() { } String beforeJava9CompatibleLocale = System.getProperty("java.locale.providers"); // and COMPAT setting needs to be first on the list - boolean isBeforeJava9Compatible = beforeJava9CompatibleLocale != null + boolean isBeforeJava9Compatible = beforeJava9CompatibleLocale != null && Strings.tokenizeToStringArray(beforeJava9CompatibleLocale, ",")[0].equals("COMPAT"); Assume.assumeTrue(isBeforeJava9Compatible); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessorTests.java index 7747bb8cae4ed..29e5d31db2172 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/QuarterProcessorTests.java @@ -7,11 +7,11 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.test.ESTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.util.TimeZone; +import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; + public class QuarterProcessorTests extends ESTestCase { private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); @@ -19,28 +19,28 @@ public class QuarterProcessorTests extends ESTestCase { public void testQuarterWithUTCTimezone() { QuarterProcessor proc = new QuarterProcessor(UTC); - assertEquals(1, proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals(4, proc.process(new DateTime(-5400, 12, 25, 10, 10, DateTimeZone.UTC))); - assertEquals(1, proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC))); - assertEquals(3, proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC))); + assertEquals(1, proc.process(dateTime(0L))); + assertEquals(4, proc.process(dateTime(-5400, 12, 25, 10, 10))); + assertEquals(1, proc.process(dateTime(30, 2, 1, 12, 13))); + assertEquals(3, proc.process(dateTime(10902, 8, 22, 11, 11))); - assertEquals(1, proc.process("0")); - assertEquals(3, proc.process("-64164233612338")); - assertEquals(2, proc.process("64164233612338")); + assertEquals(1, proc.process(dateTime(0L))); + assertEquals(3, proc.process(dateTime(-64164233612338L))); + assertEquals(2, proc.process(dateTime(64164233612338L))); } public void testValidDayNamesWithNonUTCTimeZone() { QuarterProcessor proc = new QuarterProcessor(TimeZone.getTimeZone("GMT-10:00")); - assertEquals(4, proc.process(new DateTime(0L, DateTimeZone.UTC))); - assertEquals(4, proc.process(new DateTime(-5400, 1, 1, 5, 0, DateTimeZone.UTC))); - assertEquals(1, proc.process(new DateTime(30, 4, 1, 9, 59, DateTimeZone.UTC))); + assertEquals(4, proc.process(dateTime(0L))); + assertEquals(4, proc.process(dateTime(-5400, 1, 1, 5, 0))); + assertEquals(1, proc.process(dateTime(30, 4, 1, 9, 59))); proc = new QuarterProcessor(TimeZone.getTimeZone("GMT+10:00")); - assertEquals(4, proc.process(new DateTime(10902, 9, 30, 14, 1, DateTimeZone.UTC))); - assertEquals(3, proc.process(new DateTime(10902, 9, 30, 13, 59, DateTimeZone.UTC))); + assertEquals(4, proc.process(dateTime(10902, 9, 30, 14, 1))); + assertEquals(3, proc.process(dateTime(10902, 9, 30, 13, 59))); - assertEquals(1, proc.process("0")); - assertEquals(3, proc.process("-64164233612338")); - assertEquals(2, proc.process("64164233612338")); + assertEquals(1, proc.process(dateTime(0L))); + assertEquals(3, proc.process(dateTime(-64164233612338L))); + assertEquals(2, proc.process(dateTime(64164233612338L))); } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java index e2c42874696ab..5c2b4e396acbe 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java @@ -28,7 +28,7 @@ import org.elasticsearch.xpack.sql.querydsl.query.TermsQuery; import org.elasticsearch.xpack.sql.type.EsField; import org.elasticsearch.xpack.sql.type.TypesTests; -import org.joda.time.DateTime; +import org.elasticsearch.xpack.sql.util.DateUtils; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -150,7 +150,7 @@ public void testDateRangeCast() { assertTrue(query instanceof RangeQuery); RangeQuery rq = (RangeQuery) query; assertEquals("date", rq.field()); - assertEquals(DateTime.parse("1969-05-13T12:34:56Z"), rq.lower()); + assertEquals(DateUtils.of("1969-05-13T12:34:56Z"), rq.lower()); } public void testLikeConstructsNotSupported() { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index 7a04139430e33..a6ef34ac06458 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -9,9 +9,11 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.type.DataTypeConversion.Conversion; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.util.DateUtils; +import java.time.ZonedDateTime; + +import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; public class DataTypeConversionTests extends ESTestCase { @@ -22,7 +24,7 @@ public void testConversionToString() { conversion = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); assertNull(conversion.convert(null)); - assertEquals("1970-01-01T00:00:00.000Z", conversion.convert(new DateTime(0, DateTimeZone.UTC))); + assertEquals("1970-01-01T00:00Z", conversion.convert(dateTime(0))); } /** @@ -64,33 +66,33 @@ public void testConversionToDate() { { Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, to); assertNull(conversion.convert(null)); - assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10.0)); - assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10.1)); - assertEquals(new DateTime(11L, DateTimeZone.UTC), conversion.convert(10.6)); + assertEquals(dateTime(10L), conversion.convert(10.0)); + assertEquals(dateTime(10L), conversion.convert(10.1)); + assertEquals(dateTime(11L), conversion.convert(10.6)); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); } { Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, to); assertNull(conversion.convert(null)); - assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10)); - assertEquals(new DateTime(-134L, DateTimeZone.UTC), conversion.convert(-134)); + assertEquals(dateTime(10L), conversion.convert(10)); + assertEquals(dateTime(-134L), conversion.convert(-134)); } { Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, to); assertNull(conversion.convert(null)); - assertEquals(new DateTime(1, DateTimeZone.UTC), conversion.convert(true)); - assertEquals(new DateTime(0, DateTimeZone.UTC), conversion.convert(false)); + assertEquals(dateTime(1), conversion.convert(true)); + assertEquals(dateTime(0), conversion.convert(false)); } Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, to); assertNull(conversion.convert(null)); - assertEquals(new DateTime(1000L, DateTimeZone.UTC), conversion.convert("1970-01-01T00:00:01Z")); - assertEquals(new DateTime(1483228800000L, DateTimeZone.UTC), conversion.convert("2017-01-01T00:00:00Z")); - assertEquals(new DateTime(18000000L, DateTimeZone.UTC), conversion.convert("1970-01-01T00:00:00-05:00")); + assertEquals(dateTime(1000L), conversion.convert("1970-01-01T00:00:01Z")); + assertEquals(dateTime(1483228800000L), conversion.convert("2017-01-01T00:00:00Z")); + assertEquals(dateTime(18000000L), conversion.convert("1970-01-01T00:00:00-05:00")); // double check back and forth conversion - DateTime dt = DateTime.now(DateTimeZone.UTC); + ZonedDateTime dt = ZonedDateTime.now(DateUtils.UTC); Conversion forward = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); Conversion back = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.DATE); assertEquals(dt, back.convert(forward.convert(dt))); From c6dd33c561daeb623df6fbb14fd9ac0006b37ef7 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Fri, 16 Nov 2018 19:52:16 +0200 Subject: [PATCH 2/3] Add javadocs --- .../org/elasticsearch/xpack/sql/util/DateUtils.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java index 06ace15709606..4ebc54621401a 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java @@ -26,14 +26,23 @@ public class DateUtils { private DateUtils() {} + /** + * Creates a date from the millis since epoch (thus the time-zone is UTC). + */ public static ZonedDateTime of(long millis) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC); } - + + /** + * Creates a date from the millis since epoch then translates the date into the given timezone. + */ public static ZonedDateTime of(long millis, ZoneId id) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), id); } + /** + * Parses the given string into a DateTime using UTC as a default timezone. + */ public static ZonedDateTime of(String dateFormat) { return of(UTC_DATE_FORMATTER.parseDateTime(dateFormat)); } From f1b3755d2a436be3808d98f23b7fe54402a4c967 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Sat, 17 Nov 2018 10:43:40 +0200 Subject: [PATCH 3/3] Fix ISO date printing (why, of why JDK 8?) --- .../xpack/sql/action/CliFormatter.java | 26 +++++++++--- .../xpack/sql/action/SqlQueryResponse.java | 3 +- .../xpack/sql/proto/DateUtils.java | 41 +++++++++++++++++++ .../xpack/sql/plugin/TextFormat.java | 6 ++- .../xpack/sql/util/DateUtils.java | 7 ++-- .../sql/type/DataTypeConversionTests.java | 2 +- 6 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java index c773e75aa18be..7daf768ee9cfb 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java @@ -9,8 +9,10 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.xpack.sql.proto.ColumnInfo; +import org.elasticsearch.xpack.sql.proto.DateUtils; import java.io.IOException; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -24,7 +26,7 @@ public class CliFormatter implements Writeable { * The minimum width for any column in the formatted results. */ private static final int MIN_COLUMN_WIDTH = 15; - + private int[] width; /** @@ -45,7 +47,7 @@ public CliFormatter(List columns, List> rows) { for (int i = 0; i < width.length; i++) { // TODO are we sure toString is correct here? What about dates that come back as longs. // Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081 - width[i] = Math.max(width[i], Objects.toString(row.get(i)).length()); + width[i] = Math.max(width[i], toString(row.get(i)).length()); } } } @@ -116,10 +118,10 @@ private String formatWithoutHeader(StringBuilder sb, List> rows) { if (i > 0) { sb.append('|'); } - // TODO are we sure toString is correct here? What about dates that come back as longs. // Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081 - String string = Objects.toString(row.get(i)); + String string = toString(row.get(i)); + if (string.length() <= width[i]) { // Pad sb.append(string); @@ -138,6 +140,14 @@ private String formatWithoutHeader(StringBuilder sb, List> rows) { return sb.toString(); } + private static String toString(Object object) { + if (object instanceof ZonedDateTime) { + return DateUtils.toString((ZonedDateTime) object); + } else { + return Objects.toString(object); + } + } + /** * Pick a good estimate of the buffer size needed to contain the rows. */ @@ -154,8 +164,12 @@ int estimateSize(int rows) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } CliFormatter that = (CliFormatter) o; return Arrays.equals(width, that.width); } diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index bfc76d1395d34..ff7cb02781a56 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.sql.proto.ColumnInfo; +import org.elasticsearch.xpack.sql.proto.DateUtils; import org.elasticsearch.xpack.sql.proto.Mode; import java.io.IOException; @@ -176,7 +177,7 @@ public static XContentBuilder value(XContentBuilder builder, Mode mode, Object v } // otherwise use the ISO format else { - builder.value(zdt.toLocalDateTime().toString() + zdt.getOffset().toString()); + builder.value(DateUtils.toString(zdt)); } } else { builder.value(value); diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java new file mode 100644 index 0000000000000..c087affe4ccc5 --- /dev/null +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.proto; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.util.Locale; + +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; + +public class DateUtils { + + private static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .appendLiteral('T') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .appendFraction(MILLI_OF_SECOND, 3, 3, true) + .appendOffsetId() + .toFormatter(Locale.ROOT); + + private DateUtils() {} + + + public static String toString(ZonedDateTime dateTime) { + return dateTime.format(ISO_WITH_MILLIS); + } +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java index de8798ecf544b..34c0f1c6d74f7 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java @@ -7,13 +7,15 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.xpack.sql.action.SqlQueryResponse; import org.elasticsearch.xpack.sql.action.CliFormatter; +import org.elasticsearch.xpack.sql.action.SqlQueryResponse; import org.elasticsearch.xpack.sql.proto.ColumnInfo; import org.elasticsearch.xpack.sql.session.Cursor; import org.elasticsearch.xpack.sql.session.Cursors; +import org.elasticsearch.xpack.sql.util.DateUtils; import org.elasticsearch.xpack.sql.util.StringUtils; +import java.time.ZonedDateTime; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -225,7 +227,7 @@ String format(Cursor cursor, RestRequest request, SqlQueryResponse response) { } for (List row : response.rows()) { - row(sb, row, f -> Objects.toString(f, StringUtils.EMPTY)); + row(sb, row, f -> f instanceof ZonedDateTime ? DateUtils.toString((ZonedDateTime) f) : Objects.toString(f, StringUtils.EMPTY)); } return sb.toString(); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java index 4ebc54621401a..b59b158d360be 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java @@ -59,11 +59,10 @@ public static ZonedDateTime of(DateTime dateTime) { return ZonedDateTime.ofStrict(ldt, ZoneOffset.ofTotalSeconds(dateTime.getZone().getOffset(dateTime) / 1000), - dateTime.getZone().toTimeZone().toZoneId()); + org.elasticsearch.common.time.DateUtils.dateTimeZoneToZoneId(dateTime.getZone())); } public static String toString(ZonedDateTime dateTime) { - // alternative to toOffsetDateTime().toString() - avoids creating the intermediate OffsetDateTime - return dateTime.toLocalDateTime().toString() + dateTime.getOffset().toString(); + return org.elasticsearch.xpack.sql.proto.DateUtils.toString(dateTime); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index a6ef34ac06458..49414367767c4 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -24,7 +24,7 @@ public void testConversionToString() { conversion = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); assertNull(conversion.convert(null)); - assertEquals("1970-01-01T00:00Z", conversion.convert(dateTime(0))); + assertEquals("1970-01-01T00:00:00.000Z", conversion.convert(dateTime(0))); } /**