diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java index 25a32580195..b89f6ddcc42 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java @@ -22,7 +22,9 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.def; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; +import java.time.ZonedDateTime; import java.util.Objects; /** @@ -72,11 +74,19 @@ public final class AnalyzerCaster { return PainlessCast.originalTypetoTargetType(def.class, Float.class, explicit); } else if (expected == Double.class) { return PainlessCast.originalTypetoTargetType(def.class, Double.class, explicit); + // TODO: remove this when the transition from Joda to Java datetimes is completed + } else if (expected == ZonedDateTime.class) { + return PainlessCast.originalTypetoTargetType(def.class, ZonedDateTime.class, explicit); } } else if (actual == String.class) { if (expected == char.class && explicit) { return PainlessCast.originalTypetoTargetType(String.class, char.class, true); } + // TODO: remove this when the transition from Joda to Java datetimes is completed + } else if (actual == JodaCompatibleZonedDateTime.class) { + if (expected == ZonedDateTime.class) { + return PainlessCast.originalTypetoTargetType(JodaCompatibleZonedDateTime.class, ZonedDateTime.class, explicit); + } } else if (actual == boolean.class) { if (expected == def.class) { return PainlessCast.boxOriginalType(Boolean.class, def.class, explicit, boolean.class); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 8516a96a05c..5e7bb00e426 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -23,11 +23,13 @@ import org.elasticsearch.painless.Locals.LocalMethod; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.time.ZonedDateTime; import java.util.BitSet; import java.util.Collections; import java.util.Iterator; @@ -1185,6 +1187,15 @@ public final class Def { } } + // TODO: remove this when the transition from Joda to Java datetimes is completed + public static ZonedDateTime defToZonedDateTime(final Object value) { + if (value instanceof JodaCompatibleZonedDateTime) { + return ((JodaCompatibleZonedDateTime)value).getZonedDateTime(); + } + + return (ZonedDateTime)value; + } + /** * "Normalizes" the index into a {@code Map} by making no change to the index. */ diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java index ed4cce5ddda..f6b6237a589 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java @@ -22,6 +22,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -30,6 +31,7 @@ import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import java.lang.reflect.Modifier; +import java.time.ZonedDateTime; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -71,8 +73,10 @@ import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_SHORT_EXPLICIT import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_SHORT_IMPLICIT; import static org.elasticsearch.painless.WriterConstants.DEF_TO_STRING_EXPLICIT; import static org.elasticsearch.painless.WriterConstants.DEF_TO_STRING_IMPLICIT; +import static org.elasticsearch.painless.WriterConstants.DEF_TO_ZONEDDATETIME; import static org.elasticsearch.painless.WriterConstants.DEF_UTIL_TYPE; import static org.elasticsearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE; +import static org.elasticsearch.painless.WriterConstants.JCZDT_TO_ZONEDDATETIME; import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; import static org.elasticsearch.painless.WriterConstants.MAX_INDY_STRING_CONCAT_ARGS; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; @@ -156,6 +160,9 @@ public final class MethodWriter extends GeneratorAdapter { invokeStatic(UTILITY_TYPE, CHAR_TO_STRING); } else if (cast.originalType == String.class && cast.targetType == char.class) { invokeStatic(UTILITY_TYPE, STRING_TO_CHAR); + // TODO: remove this when the transition from Joda to Java datetimes is completed + } else if (cast.originalType == JodaCompatibleZonedDateTime.class && cast.targetType == ZonedDateTime.class) { + invokeStatic(UTILITY_TYPE, JCZDT_TO_ZONEDDATETIME); } else if (cast.unboxOriginalType != null && cast.boxTargetType != null) { unbox(getType(cast.unboxOriginalType)); writeCast(cast.unboxOriginalType, cast.boxTargetType); @@ -191,6 +198,8 @@ public final class MethodWriter extends GeneratorAdapter { else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_FLOAT_EXPLICIT); else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_DOUBLE_EXPLICIT); else if (cast.targetType == String.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_STRING_EXPLICIT); + // TODO: remove this when the transition from Joda to Java datetimes is completed + else if (cast.targetType == ZonedDateTime.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_ZONEDDATETIME); else { writeCast(cast.originalType, cast.targetType); } @@ -212,6 +221,8 @@ public final class MethodWriter extends GeneratorAdapter { else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_FLOAT_IMPLICIT); else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_DOUBLE_IMPLICIT); else if (cast.targetType == String.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_STRING_IMPLICIT); + // TODO: remove this when the transition from Joda to Java datetimes is completed + else if (cast.targetType == ZonedDateTime.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_ZONEDDATETIME); else { writeCast(cast.originalType, cast.targetType); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Utility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Utility.java index a03a7b244eb..7588ad0f96e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Utility.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Utility.java @@ -19,6 +19,10 @@ package org.elasticsearch.painless; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; + +import java.time.ZonedDateTime; + /** * A set of methods for non-native boxing and non-native * exact math operations used at both compile-time and runtime. @@ -43,5 +47,10 @@ public class Utility { return value.charAt(0); } + // TODO: remove this when the transition from Joda to Java datetimes is completed + public static ZonedDateTime JCZDTToZonedDateTime(final JodaCompatibleZonedDateTime jczdt) { + return jczdt.getZonedDateTime(); + } + private Utility() {} } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 7979f29cb2e..7a10283a996 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -21,6 +21,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.api.Augmentation; import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.elasticsearch.script.ScriptException; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; @@ -31,6 +32,7 @@ import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.time.ZonedDateTime; import java.util.BitSet; import java.util.Collection; import java.util.Collections; @@ -98,13 +100,16 @@ public final class WriterConstants { public static final Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class); public static final Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class); + // TODO: remove this when the transition from Joda to Java datetimes is completed + public static final Method JCZDT_TO_ZONEDDATETIME = + getAsmMethod(ZonedDateTime.class, "JCZDTToZonedDateTime", JodaCompatibleZonedDateTime.class); public static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class); /** - * A Method instance for {@linkplain Pattern#compile}. This isn't available from PainlessLookup because we intentionally don't add it + * A Method instance for {@linkplain Pattern}. This isn't available from PainlessLookup because we intentionally don't add it * there so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building * regexes because it can do it statically. This is both faster and prevents the script from doing something super slow like building a * regex per time it is run. @@ -161,6 +166,9 @@ public final class WriterConstants { public static final Method DEF_TO_STRING_IMPLICIT = getAsmMethod(String.class, "defToStringImplicit", Object.class); public static final Method DEF_TO_STRING_EXPLICIT = getAsmMethod(String.class, "defToStringExplicit", Object.class); + // TODO: remove this when the transition from Joda to Java datetimes is completed + public static final Method DEF_TO_ZONEDDATETIME = getAsmMethod(ZonedDateTime.class, "defToZonedDateTime", Object.class); + public static final Type DEF_ARRAY_LENGTH_METHOD_TYPE = Type.getMethodType(Type.INT_TYPE, Type.getType(Object.class)); /** invokedynamic bootstrap for lambda expression/method references */ diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt index c1da194b98f..c7c0a31127a 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt @@ -87,7 +87,6 @@ class org.elasticsearch.script.JodaCompatibleZonedDateTime { int getNano() int getSecond() int getYear() - ZoneId getZone() ZonedDateTime minus(TemporalAmount) ZonedDateTime minus(long,TemporalUnit) ZonedDateTime minusYears(long) @@ -108,7 +107,6 @@ class org.elasticsearch.script.JodaCompatibleZonedDateTime { ZonedDateTime plusSeconds(long) ZonedDateTime plusWeeks(long) ZonedDateTime plusYears(long) - Instant toInstant() OffsetDateTime toOffsetDateTime() ZonedDateTime truncatedTo(TemporalUnit) ZonedDateTime with(TemporalAdjuster) @@ -127,25 +125,6 @@ class org.elasticsearch.script.JodaCompatibleZonedDateTime { ZonedDateTime withZoneSameLocal(ZoneId) ZonedDateTime withZoneSameInstant(ZoneId) - #### ChronoZonedDateTime - int compareTo(JodaCompatibleZonedDateTime) - Chronology getChronology() - String format(DateTimeFormatter) - int get(TemporalField) - long getLong(TemporalField) - ZoneOffset getOffset() - boolean isSupported(TemporalField) - long toEpochSecond() - LocalTime toLocalTime() - - #### Joda methods that exist in java time - boolean equals(Object) - int hashCode() - boolean isAfter(JodaCompatibleZonedDateTime) - boolean isBefore(JodaCompatibleZonedDateTime) - boolean isEqual(JodaCompatibleZonedDateTime) - String toString() - #### Joda time methods long getMillis() int getCenturyOfEra() diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/BasicAPITests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BasicAPITests.java index 371c3a5a3e5..1be1d782b1c 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/BasicAPITests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BasicAPITests.java @@ -139,4 +139,14 @@ public class BasicAPITests extends ScriptTestCase { assertEquals(10, exec("staticAddIntsTest(7, 3)")); assertEquals(15.5f, exec("staticAddFloatsTest(6.5f, 9.0f)")); } + + // TODO: remove this when the transition from Joda to Java datetimes is completed + public void testJCZDTToZonedDateTime() { + assertEquals(0L, exec( + "Instant instant = Instant.ofEpochMilli(434931330000L);" + + "JodaCompatibleZonedDateTime d = new JodaCompatibleZonedDateTime(instant, ZoneId.of('Z'));" + + "ZonedDateTime t = d;" + + "return ChronoUnit.MILLIS.between(d, t);" + )); + } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefCastTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefCastTests.java index c01cdcd2c97..cb9fcc9de70 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefCastTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefCastTests.java @@ -683,4 +683,14 @@ public class DefCastTests extends ScriptTestCase { public void testdefToStringExplicit() { assertEquals("s", exec("def d = (char)'s'; String b = (String)d; b")); } + + // TODO: remove this when the transition from Joda to Java datetimes is completed + public void testdefToZonedDateTime() { + assertEquals(0L, exec( + "Instant instant = Instant.ofEpochMilli(434931330000L);" + + "def d = new JodaCompatibleZonedDateTime(instant, ZoneId.of('Z'));" + + "ZonedDateTime t = d;" + + "return ChronoUnit.MILLIS.between(d, t);" + )); + } } diff --git a/modules/lang-painless/src/test/resources/org/elasticsearch/painless/spi/org.elasticsearch.painless.test b/modules/lang-painless/src/test/resources/org/elasticsearch/painless/spi/org.elasticsearch.painless.test index 3ac8fa0ee0c..1d27b7e4431 100644 --- a/modules/lang-painless/src/test/resources/org/elasticsearch/painless/spi/org.elasticsearch.painless.test +++ b/modules/lang-painless/src/test/resources/org/elasticsearch/painless/spi/org.elasticsearch.painless.test @@ -1,4 +1,10 @@ # whitelist for tests + +# TODO: remove this when the transition from Joda to Java datetimes is completed +class org.elasticsearch.script.JodaCompatibleZonedDateTime { + (Instant, ZoneId) +} + class org.elasticsearch.painless.BindingsTests$BindingsTestScript { } diff --git a/server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java b/server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java index 017acbf4951..2c09456ed43 100644 --- a/server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java +++ b/server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java @@ -38,13 +38,18 @@ import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; +import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.Locale; import java.util.Objects; @@ -52,7 +57,9 @@ import java.util.Objects; /** * A wrapper around ZonedDateTime that exposes joda methods for backcompat. */ -public class JodaCompatibleZonedDateTime { +public class JodaCompatibleZonedDateTime + implements Comparable>, ChronoZonedDateTime, Temporal, TemporalAccessor { + private static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("strict_date_time"); private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(JodaCompatibleZonedDateTime.class)); @@ -83,9 +90,15 @@ public class JodaCompatibleZonedDateTime { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - JodaCompatibleZonedDateTime that = (JodaCompatibleZonedDateTime) o; - return Objects.equals(dt, that.dt); + if (o == null)return false; + if (o.getClass() == JodaCompatibleZonedDateTime.class) { + JodaCompatibleZonedDateTime that = (JodaCompatibleZonedDateTime) o; + return Objects.equals(dt, that.dt); + } else if (o.getClass() == ZonedDateTime.class) { + ZonedDateTime that = (ZonedDateTime) o; + return Objects.equals(dt, that); + } + return false; } @Override @@ -98,56 +111,76 @@ public class JodaCompatibleZonedDateTime { return DATE_FORMATTER.format(dt); } + @Override public String format(DateTimeFormatter formatter) { return dt.format(formatter); } + @Override + public ValueRange range(TemporalField field) { + return dt.range(field); + } + + @Override public int get(TemporalField field) { return dt.get(field); } + @Override public long getLong(TemporalField field) { return dt.getLong(field); } + @Override public Chronology getChronology() { return dt.getChronology(); } - public int compareTo(JodaCompatibleZonedDateTime o) { - return dt.compareTo(o.dt); - } - + @Override public ZoneOffset getOffset() { return dt.getOffset(); } + @Override public boolean isSupported(TemporalField field) { return dt.isSupported(field); } + @Override + public boolean isSupported(TemporalUnit unit) { + return dt.isSupported(unit); + } + + @Override public long toEpochSecond() { return dt.toEpochSecond(); } + @Override + public int compareTo(ChronoZonedDateTime other) { + return dt.compareTo(other); + } + + @Override + public boolean isBefore(ChronoZonedDateTime other) { + return dt.isBefore(other); + } + + @Override + public boolean isAfter(ChronoZonedDateTime other) { + return dt.isAfter(other); + } + + @Override + public boolean isEqual(ChronoZonedDateTime other) { + return dt.isEqual(other); + } + + @Override public LocalTime toLocalTime() { return dt.toLocalTime(); } - public boolean isAfter(JodaCompatibleZonedDateTime o) { - return dt.isAfter(o.getZonedDateTime()); - } - - public boolean isBefore(JodaCompatibleZonedDateTime o) { - return dt.isBefore(o.getZonedDateTime()); - } - - public boolean isEqual(JodaCompatibleZonedDateTime o) { - return dt.isEqual(o.getZonedDateTime()); - } - - - public int getDayOfMonth() { return dt.getDayOfMonth(); } @@ -160,10 +193,12 @@ public class JodaCompatibleZonedDateTime { return dt.getHour(); } + @Override public LocalDate toLocalDate() { return dt.toLocalDate(); } + @Override public LocalDateTime toLocalDateTime() { return dt.toLocalDateTime(); } @@ -192,18 +227,31 @@ public class JodaCompatibleZonedDateTime { return dt.getYear(); } + @Override public ZoneId getZone() { return dt.getZone(); } + @Override public ZonedDateTime minus(TemporalAmount delta) { return dt.minus(delta); } + @Override public ZonedDateTime minus(long amount, TemporalUnit unit) { return dt.minus(amount, unit); } + @Override + public R query(TemporalQuery query) { + return dt.query(query); + } + + @Override + public long until(Temporal temporal, TemporalUnit temporalUnit) { + return dt.until(temporal, temporalUnit); + } + public ZonedDateTime minusYears(long amount) { return dt.minusYears(amount); } @@ -236,10 +284,12 @@ public class JodaCompatibleZonedDateTime { return dt.minusNanos(amount); } + @Override public ZonedDateTime plus(TemporalAmount amount) { return dt.plus(amount); } + @Override public ZonedDateTime plus(long amount,TemporalUnit unit) { return dt.plus(amount, unit); } @@ -276,6 +326,7 @@ public class JodaCompatibleZonedDateTime { return dt.plusYears(amount); } + @Override public Instant toInstant() { return dt.toInstant(); } @@ -289,10 +340,12 @@ public class JodaCompatibleZonedDateTime { return dt.truncatedTo(unit); } + @Override public ZonedDateTime with(TemporalAdjuster adjuster) { return dt.with(adjuster); } + @Override public ZonedDateTime with(TemporalField field, long newValue) { return dt.with(field, newValue); } @@ -305,6 +358,7 @@ public class JodaCompatibleZonedDateTime { return dt.withDayOfYear(value); } + @Override public ZonedDateTime withEarlierOffsetAtOverlap() { return dt.withEarlierOffsetAtOverlap(); } @@ -317,6 +371,7 @@ public class JodaCompatibleZonedDateTime { return dt.withHour(value); } + @Override public ZonedDateTime withLaterOffsetAtOverlap() { return dt.withLaterOffsetAtOverlap(); } @@ -341,10 +396,12 @@ public class JodaCompatibleZonedDateTime { return dt.withYear(value); } + @Override public ZonedDateTime withZoneSameLocal(ZoneId zone) { return dt.withZoneSameLocal(zone); } + @Override public ZonedDateTime withZoneSameInstant(ZoneId zone) { return dt.withZoneSameInstant(zone); }