This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch graphbinary-add-datetime in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 34f759f5907de1f9fc0b9e2763307a6f1b4ff277 Author: Yang Xia <[email protected]> AuthorDate: Thu Sep 12 09:20:59 2024 -0700 implement graphbinary datetime serializers for java and python --- .../gremlin/language/grammar/ArgumentVisitor.java | 6 +- .../language/grammar/GenericLiteralVisitor.java | 6 +- .../translator/DotNetTranslateVisitor.java | 6 +- .../language/translator/GoTranslateVisitor.java | 6 +- .../language/translator/JavaTranslateVisitor.java | 9 +- .../translator/JavascriptTranslateVisitor.java | 7 +- .../translator/PythonTranslateVisitor.java | 6 +- .../gremlin/process/traversal/GremlinLang.java | 5 +- .../traversal/dsl/graph/GraphTraversal.java | 18 +-- .../gremlin/process/traversal/dsl/graph/__.java | 11 +- .../process/traversal/step/map/AsDateStep.java | 24 ++- .../process/traversal/step/map/DateAddStep.java | 49 ++++--- .../process/traversal/step/map/DateDiffStep.java | 23 +-- .../gremlin/structure/io/binary/DataType.java | 3 +- .../io/binary/TypeSerializerRegistry.java | 5 +- ...TimeSerializer.java => DateTimeSerializer.java} | 20 ++- .../tinkerpop/gremlin/util/DatetimeHelper.java | 26 ++-- .../grammar/GeneralLiteralVisitorTest.java | 39 +++-- .../language/translator/GremlinTranslatorTest.java | 10 +- .../gremlin/process/traversal/OrderTest.java | 9 +- .../process/traversal/step/map/AsDateStepTest.java | 16 +- .../traversal/step/map/DateAddStepTest.java | 45 ++---- .../traversal/step/map/DateDiffStepTest.java | 44 ++---- .../tinkerpop/gremlin/util/DatetimeHelperTest.java | 54 +++---- .../src/main/python/gremlin_python/statics.py | 8 - .../gremlin_python/structure/io/graphbinaryV4.py | 45 +++++- .../src/main/python/radish/feature_steps.py | 2 +- gremlin-python/src/main/python/radish/gremlin.py | 19 ++- .../tests/structure/io/test_functionalityio.py | 28 +--- .../tests/structure/io/test_graphbinaryV4.py | 27 +++- .../gremlin/test/features/map/AsDate.feature | 162 ++++++++++----------- .../gremlin/test/features/map/DateAdd.feature | 144 +++++++++--------- .../util/ser/binary/types/sample/SamplePerson.java | 8 +- .../types/sample/SamplePersonSerializer.java | 4 +- .../types/sample/SamplePersonSerializerTest.java | 8 +- 35 files changed, 461 insertions(+), 441 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java index 2a3b214bba..b6b463b627 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java @@ -33,8 +33,8 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import java.lang.reflect.Array; +import java.time.OffsetDateTime; import java.util.Comparator; -import java.util.Date; import java.util.Map; import java.util.function.Function; @@ -80,8 +80,8 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> { /** * Wrapper to visit function for Date type. */ - public Date parseDate(final GremlinParser.DateArgumentContext ctx) { - return (Date) visitDateArgument(ctx); + public OffsetDateTime parseDate(final GremlinParser.DateArgumentContext ctx) { + return (OffsetDateTime) visitDateArgument(ctx); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java index f29c563d3b..ced6c832d1 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java @@ -31,8 +31,8 @@ import org.apache.tinkerpop.gremlin.util.DatetimeHelper; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -86,8 +86,8 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> { /** * Parse a Date based literal context and return the Date. */ - public Date parseDate(final GremlinParser.DateLiteralContext dateLiteral) { - return (Date) visitDateLiteral(dateLiteral); + public OffsetDateTime parseDate(final GremlinParser.DateLiteralContext dateLiteral) { + return (OffsetDateTime) visitDateLiteral(dateLiteral); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java index 2701819624..c7ccb15323 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; +import java.time.OffsetDateTime; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -150,9 +151,10 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor { public Void visitDateLiteral(final GremlinParser.DateLiteralContext ctx) { // child at 2 is the date argument to datetime() and comes enclosed in quotes final String dtString = ctx.getChild(2).getText(); - final Date dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + final OffsetDateTime dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + // todo: update when dotnet datetime serializer is implemented sb.append("DateTimeOffset.FromUnixTimeMilliseconds("); - sb.append(dt.getTime()); + sb.append(dt.toInstant().toEpochMilli()); sb.append(")"); return null; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java index 10eec9f3e0..10a8e7e4ab 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; +import java.time.OffsetDateTime; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -47,8 +48,9 @@ public class GoTranslateVisitor extends AbstractTranslateVisitor { public Void visitDateLiteral(final GremlinParser.DateLiteralContext ctx) { // child at 2 is the date argument to datetime() and comes enclosed in quotes final String dtString = ctx.getChild(2).getText(); - final Date dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); - sb.append("time.UnixMilli(" + dt.getTime() + ")"); + final OffsetDateTime dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + // todo: update when go datetime serializer is implemented + sb.append("time.UnixMilli(" + dt.toInstant().toEpochMilli() + ")"); return null; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java index 9eb0e945e5..9830d8c95c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser; import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; +import java.time.OffsetDateTime; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @@ -144,10 +145,10 @@ public class JavaTranslateVisitor extends AbstractTranslateVisitor { public Void visitDateLiteral(final GremlinParser.DateLiteralContext ctx) { // child at 2 is the date argument to datetime() and comes enclosed in quotes final String dtString = ctx.getChild(2).getText(); - final Date dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); - sb.append("new Date("); - sb.append(dt.getTime()); - sb.append(")"); + final OffsetDateTime dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + sb.append("OffsetDateTime.parse(\""); + sb.append(dt); + sb.append("\")"); return null; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java index 01d094fb68..830d6211d7 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java @@ -27,7 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; -import java.util.Date; +import java.time.OffsetDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -133,9 +133,10 @@ public class JavascriptTranslateVisitor extends AbstractTranslateVisitor { public Void visitDateLiteral(final GremlinParser.DateLiteralContext ctx) { // child at 2 is the date argument to datetime() and comes enclosed in quotes final String dtString = ctx.getChild(2).getText(); - final Date dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + final OffsetDateTime dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + // todo: update when javascript datetime serializer is implemented sb.append("new Date("); - sb.append(dt.getTime()); + sb.append(dt.toInstant().toEpochMilli()); sb.append(")"); return null; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java index 092c41fe5e..46cebbbc4d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java @@ -27,7 +27,7 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; import java.math.BigInteger; -import java.util.Date; +import java.time.OffsetDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -161,8 +161,8 @@ public class PythonTranslateVisitor extends AbstractTranslateVisitor { public Void visitDateLiteral(final GremlinParser.DateLiteralContext ctx) { // child at 2 is the date argument to datetime() and comes enclosed in quotes final String dtString = ctx.getChild(2).getText(); - final Date dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); - sb.append("datetime.datetime.utcfromtimestamp(" + dt.getTime() + " / 1000.0)"); + final OffsetDateTime dt = DatetimeHelper.parse(removeFirstAndLastCharacters(dtString)); + sb.append("datetime.datetime.fromtimestamp(" + dt.toEpochSecond() + ").astimezone(datetime.timezone.utc)"); return null; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java index 1b50dee8c7..4fd9eed353 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java @@ -34,6 +34,7 @@ import javax.lang.model.SourceVersion; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -134,8 +135,8 @@ public class GremlinLang implements Cloneable, Serializable { if (arg instanceof BigDecimal) return String.format("%sM", arg); - if (arg instanceof Date) - return String.format("datetime(\"%s\")", format(((Date) arg).toInstant())); + if (arg instanceof OffsetDateTime) + return String.format("datetime(\"%s\")", format(((OffsetDateTime) arg).toInstant())); if (arg instanceof Enum) { // special handling for enums with additional interfaces diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index df8e75f0dd..36dcc5dd5f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -205,12 +205,12 @@ import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.function.ConstantSupplier; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -1841,49 +1841,49 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { } /** - * Parse value of the incoming traverser as an ISO-8601 {@link Date}. + * Parse value of the incoming traverser as an ISO-8601 {@link OffsetDateTime}. * * @return the traversal with an appended {@link AsDateStep}. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#asDate-step" target="_blank">Reference Documentation - asDate Step</a> * @since 3.7.1 */ - public default GraphTraversal<S, Date> asDate() { + public default GraphTraversal<S, OffsetDateTime> asDate() { this.asAdmin().getGremlinLang().addStep(Symbols.asDate); return this.asAdmin().addStep(new AsDateStep<>(this.asAdmin())); } /** - * Increase value of input {@link Date}. + * Increase value of input {@link OffsetDateTime}. * * @return the traversal with an appended {@link DateAddStep}. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#dateAdd-step" target="_blank">Reference Documentation - dateAdd Step</a> * @since 3.7.1 */ - public default GraphTraversal<S, Date> dateAdd(final DT dateToken, final int value) { + public default GraphTraversal<S, OffsetDateTime> dateAdd(final DT dateToken, final int value) { this.asAdmin().getGremlinLang().addStep(Symbols.dateAdd, dateToken, value); return this.asAdmin().addStep(new DateAddStep<>(this.asAdmin(), dateToken, value)); } /** - * Returns the difference between two {@link Date} in epoch time. + * Returns the difference between two {@link OffsetDateTime} in epoch time. * * @return the traversal with an appended {@link DateDiffStep}. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#dateDiff-step" target="_blank">Reference Documentation - dateDiff Step</a> * @since 3.7.1 */ - public default GraphTraversal<S, Long> dateDiff(final Date value) { + public default GraphTraversal<S, Long> dateDiff(final OffsetDateTime value) { this.asAdmin().getGremlinLang().addStep(Symbols.dateDiff, value); return this.asAdmin().addStep(new DateDiffStep<>(this.asAdmin(), value)); } /** - * Returns the difference between two {@link Date} in epoch time. + * Returns the difference between two {@link OffsetDateTime} in epoch time. * * @return the traversal with an appended {@link DateDiffStep}. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#dateDiff-step" target="_blank">Reference Documentation - dateDiff Step</a> * @since 3.7.1 */ - public default GraphTraversal<S, Long> dateDiff(final Traversal<?, Date> dateTraversal) { + public default GraphTraversal<S, Long> dateDiff(final Traversal<?, OffsetDateTime> dateTraversal) { this.asAdmin().getGremlinLang().addStep(Symbols.dateDiff, dateTraversal); return this.asAdmin().addStep(new DateDiffStep<>(this.asAdmin(), dateTraversal)); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java index 14a0e9dfb5..57776db82e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java @@ -36,6 +36,7 @@ import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import java.time.OffsetDateTime; import java.util.Collection; import java.util.Date; import java.util.Iterator; @@ -773,28 +774,28 @@ public class __ { /** * @see GraphTraversal#asDate() */ - public static <A> GraphTraversal<A, Date> asDate() { + public static <A> GraphTraversal<A, OffsetDateTime> asDate() { return __.<A>start().asDate(); } /** * @see GraphTraversal#dateAdd(DT, int) */ - public static <A> GraphTraversal<A, Date> dateAdd(final DT dateToken, final int value) { + public static <A> GraphTraversal<A, OffsetDateTime> dateAdd(final DT dateToken, final int value) { return __.<A>start().dateAdd(dateToken, value); } /** - * @see GraphTraversal#dateDiff(Date) + * @see GraphTraversal#dateDiff(OffsetDateTime) */ - public static <A> GraphTraversal<A, Long> dateDiff(final Date value) { + public static <A> GraphTraversal<A, Long> dateDiff(final OffsetDateTime value) { return __.<A>start().dateDiff(value); } /** * @see GraphTraversal#dateDiff(Traversal) */ - public static <A> GraphTraversal<A, Long> dateDiff(final Traversal<?, Date> dateTraversal) { + public static <A> GraphTraversal<A, Long> dateDiff(final Traversal<?, OffsetDateTime> dateTraversal) { return __.<A>start().dateDiff(dateTraversal); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java index 22ea8db3f3..1359ee5d82 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java @@ -20,17 +20,15 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; -import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; -import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; -import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.util.DatetimeHelper; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.time.format.DateTimeParseException; import java.util.Collections; -import java.util.Date; -import java.util.List; import java.util.Set; /** @@ -38,32 +36,32 @@ import java.util.Set; * * @author Valentyn Kahamlyk */ -public final class AsDateStep<S> extends ScalarMapStep<S, Date> { +public final class AsDateStep<S> extends ScalarMapStep<S, OffsetDateTime> { public AsDateStep(final Traversal.Admin traversal) { super(traversal); } @Override - protected Date map(final Traverser.Admin<S> traverser) { + protected OffsetDateTime map(final Traverser.Admin<S> traverser) { final Object object = traverser.get(); if (object == null) - throw new IllegalArgumentException("Can't parse null as Date."); - if (object instanceof Date) - return (Date) object; + throw new IllegalArgumentException("Can't parse null as DateTime."); + if (object instanceof OffsetDateTime) + return (OffsetDateTime) object; if (object instanceof Number) // numbers handled as milliseconds since January 1, 1970, 00:00:00 GMT. - return new Date(((Number) object).longValue()); + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(((Number) object).longValue()), ZoneOffset.UTC); if (object instanceof String) { try { return DatetimeHelper.parse((String) object); } catch (DateTimeParseException e) { - throw new IllegalArgumentException("Can't parse " + object + " as Date."); + throw new IllegalArgumentException("Can't parse " + object + " as OffsetDateTime."); } } - throw new IllegalArgumentException("Can't parse " + object.getClass().getName() + " as Date."); + throw new IllegalArgumentException("Can't parse " + object.getClass().getName() + " as OffsetDateTime."); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStep.java index ef45701d1f..9dcedcd8c6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStep.java @@ -24,11 +24,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import java.util.Calendar; +import java.time.Duration; +import java.time.OffsetDateTime; import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; import java.util.Set; /** @@ -36,16 +34,7 @@ import java.util.Set; * * @author Valentyn Kahamlyk */ -public final class DateAddStep<S> extends ScalarMapStep<S, Date> { - - final static Map<DT, Integer> DTtoCalendar = new HashMap<>(); - - static { - DTtoCalendar.put(DT.second, Calendar.SECOND); - DTtoCalendar.put(DT.minute, Calendar.MINUTE); - DTtoCalendar.put(DT.hour, Calendar.HOUR_OF_DAY); - DTtoCalendar.put(DT.day, Calendar.DAY_OF_MONTH); - } +public final class DateAddStep<S> extends ScalarMapStep<S, OffsetDateTime> { private DT dateToken; private int value; @@ -57,16 +46,32 @@ public final class DateAddStep<S> extends ScalarMapStep<S, Date> { } @Override - protected Date map(final Traverser.Admin<S> traverser) { + protected OffsetDateTime map(final Traverser.Admin<S> traverser) { final Object object = traverser.get(); - if (!(object instanceof Date)) throw new IllegalArgumentException("dateAdd accept only Date."); - - final Calendar cal = Calendar.getInstance(); - cal.setTime((Date) object); - cal.add(DTtoCalendar.get(dateToken), value); - - return cal.getTime(); + if (!(object instanceof OffsetDateTime)) throw new IllegalArgumentException("dateAdd accept only DateTime."); + + OffsetDateTime date = (OffsetDateTime) object; + OffsetDateTime new_date; + + switch (dateToken) { + case second: + new_date = date.plus(Duration.ofSeconds(value)); + break; + case minute: + new_date = date.plus(Duration.ofMinutes(value)); + break; + case hour: + new_date = date.plus(Duration.ofHours(value)); + break; + case day: + new_date = date.plus(Duration.ofDays(value)); + break; + default: + throw new IllegalArgumentException("DT tokens should only be second, minute, hour, or day."); + } + + return new_date; } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStep.java index d15d461845..63c2c93ee0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStep.java @@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequire import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import java.time.OffsetDateTime; import java.util.Collections; import java.util.Date; import java.util.Set; @@ -36,15 +37,15 @@ import java.util.Set; */ public final class DateDiffStep<S> extends ScalarMapStep<S, Long> implements TraversalParent { - private Date value; - private Traversal.Admin<S, Date> dateTraversal; + private OffsetDateTime value; + private Traversal.Admin<S, OffsetDateTime> dateTraversal; - public DateDiffStep(final Traversal.Admin traversal, final Date value) { + public DateDiffStep(final Traversal.Admin traversal, final OffsetDateTime value) { super(traversal); this.value = value; } - public DateDiffStep(final Traversal.Admin traversal, final Traversal<?, Date> dateTraversal) { + public DateDiffStep(final Traversal.Admin traversal, final Traversal<?, OffsetDateTime> dateTraversal) { super(traversal); this.dateTraversal = this.integrateChild(dateTraversal.asAdmin()); } @@ -53,17 +54,17 @@ public final class DateDiffStep<S> extends ScalarMapStep<S, Long> implements Tra protected Long map(final Traverser.Admin<S> traverser) { final Object object = traverser.get(); - if (!(object instanceof Date)) + if (!(object instanceof OffsetDateTime)) throw new IllegalArgumentException( - String.format("DateDiff can only take Date as argument, encountered %s", object.getClass())); + String.format("DateDiff can only take DateTime as argument, encountered %s", object.getClass())); - final Date otherDate = value != null ? value : + final OffsetDateTime otherDate = value != null ? value : dateTraversal != null ? TraversalUtil.apply(traverser, dateTraversal) : null; // let's not throw exception and assume null date == 0 - final long otherDateMs = otherDate == null ? 0 : otherDate.getTime(); + final long otherDateMs = otherDate == null ? 0 : otherDate.toEpochSecond(); - return (((Date) object).getTime() - otherDateMs) / 1000; + return (((OffsetDateTime) object).toEpochSecond() - otherDateMs); } @Override @@ -96,11 +97,11 @@ public final class DateDiffStep<S> extends ScalarMapStep<S, Long> implements Tra return StringFactory.stepString(this); } - public Date getValue() { + public OffsetDateTime getValue() { return this.value; } - public Traversal.Admin<S, Date> getDateTraversal() { + public Traversal.Admin<S, OffsetDateTime> getDateTraversal() { return this.dateTraversal; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java index 4f52b6069d..90548c4cdd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java @@ -28,7 +28,7 @@ public enum DataType { INT(0x01), LONG(0x02), STRING(0X03), - DATETIME(0X04), // todo: to be updated from OffsetDateTime + DATETIME(0X04), DOUBLE(0X07), FLOAT(0X08), LIST(0X09), @@ -55,7 +55,6 @@ public enum DataType { CHAR(0X80), DURATION(0X81), - OFFSETDATETIME(0X88), CUSTOM(0), MARKER(0XFD), diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java index 427de19c28..db6193d266 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java @@ -44,7 +44,7 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.types.GraphSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.ListSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.MapEntrySerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.MapSerializer; -import org.apache.tinkerpop.gremlin.structure.io.binary.types.OffsetDateTimeSerializer; +import org.apache.tinkerpop.gremlin.structure.io.binary.types.DateTimeSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.PathSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.PropertySerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.SetSerializer; @@ -120,8 +120,7 @@ public class TypeSerializerRegistry { new RegistryEntry<>(Character.class, new CharSerializer()), new RegistryEntry<>(Duration.class, new DurationSerializer()), - new RegistryEntry<>(OffsetDateTime.class, new OffsetDateTimeSerializer()), //todo: to be updated to DateTime serializer - + new RegistryEntry<>(OffsetDateTime.class, new DateTimeSerializer()) }; public static final TypeSerializerRegistry INSTANCE = build().create(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/OffsetDateTimeSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/DateTimeSerializer.java similarity index 68% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/OffsetDateTimeSerializer.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/DateTimeSerializer.java index 96019c1a20..416ceb8aa1 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/OffsetDateTimeSerializer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/DateTimeSerializer.java @@ -24,29 +24,33 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; import org.apache.tinkerpop.gremlin.structure.io.Buffer; import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public class OffsetDateTimeSerializer extends SimpleTypeSerializer<OffsetDateTime> { - public OffsetDateTimeSerializer() { - super(DataType.OFFSETDATETIME); +public class DateTimeSerializer extends SimpleTypeSerializer<OffsetDateTime> { + public DateTimeSerializer() { + super(DataType.DATETIME); } @Override protected OffsetDateTime readValue(final Buffer buffer, final GraphBinaryReader context) throws IOException { - // todo: Updates needed for deserializing these values as their serializers are removed - final LocalDateTime ldt = context.readValue(buffer, LocalDateTime.class, false); - final ZoneOffset zo = context.readValue(buffer, ZoneOffset.class, false); + final LocalDate localDate = LocalDate.of(buffer.readInt(), buffer.readByte(), buffer.readByte()); + final LocalTime localTime = LocalTime.ofNanoOfDay(buffer.readLong()); + final LocalDateTime ldt = LocalDateTime.of(localDate, localTime); + final ZoneOffset zo = ZoneOffset.ofTotalSeconds(buffer.readInt()); return OffsetDateTime.of(ldt, zo); } @Override protected void writeValue(final OffsetDateTime value, final Buffer buffer, final GraphBinaryWriter context) throws IOException { - context.writeValue(value.toLocalDateTime(), buffer, false); - context.writeValue(value.getOffset(), buffer, false); + buffer.writeInt(value.getYear()).writeByte(value.getMonthValue()).writeByte(value.getDayOfMonth()); + buffer.writeLong(value.toLocalTime().toNanoOfDay()); + buffer.writeInt(value.getOffset().getTotalSeconds()); } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/DatetimeHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/DatetimeHelper.java index 31334117b0..88f7e1c332 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/DatetimeHelper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/DatetimeHelper.java @@ -22,7 +22,11 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.Month; +import java.time.OffsetDateTime; +import java.time.Year; import java.time.YearMonth; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -93,45 +97,41 @@ public final class DatetimeHelper { * </ul>> * */ - public static Date parse(final String d) { + public static OffsetDateTime parse(final String d) { final TemporalAccessor t = formatter.parse(d); if (!t.isSupported(ChronoField.HOUR_OF_DAY)) { // no hours field so it must be a Date or a YearMonth if (!t.isSupported(ChronoField.DAY_OF_MONTH)) { // must be a YearMonth coz no day - return Date.from(YearMonth.from(t).atDay(1).atStartOfDay(UTC).toInstant()); + return OffsetDateTime.of(LocalDate.of(Year.from(t).getValue(), Month.from(t), 1), LocalTime.MIDNIGHT, UTC); } else { // must be a Date as the day is present - return Date.from(Instant.ofEpochSecond(LocalDate.from(t).atStartOfDay().toEpochSecond(UTC))); + return OffsetDateTime.of(LocalDate.from(t), LocalTime.MIDNIGHT, UTC); } } else if (!t.isSupported(ChronoField.MONTH_OF_YEAR)) { // no month field so must be a Time - final Instant timeOnEpochDay = LocalDate.ofEpochDay(0) - .atTime(LocalTime.from(t)) - .atZone(UTC) - .toInstant(); - return Date.from(timeOnEpochDay); + return OffsetDateTime.of(LocalDate.ofEpochDay(0), LocalTime.from(t), UTC); } else if (t.isSupported(ChronoField.OFFSET_SECONDS)) { // has all datetime components including an offset - return Date.from(ZonedDateTime.from(t).toInstant()); + return OffsetDateTime.of(LocalDateTime.from(t), ZoneOffset.from(t)); } else { // has all datetime components but no offset so throw in some UTC - return Date.from(ZonedDateTime.of(LocalDateTime.from(t), UTC).toInstant()); + return OffsetDateTime.of(LocalDateTime.from(t), UTC); } } /** * A proxy call to {@link #parse(String)} but allows for syntax similar to Gremlin grammar of {@code datetime()}. */ - public static Date datetime(final String d) { + public static OffsetDateTime datetime(final String d) { return parse(d); } /** * A proxy allows for syntax similar to Gremlin grammar of {@code datetime()}. */ - public static Date datetime() { - return new Date(); + public static OffsetDateTime datetime() { + return OffsetDateTime.now(); } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java index dea62a082c..e9d2f5d2a5 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java @@ -31,17 +31,17 @@ import org.junit.runners.Parameterized; import java.lang.reflect.Constructor; import java.math.BigDecimal; import java.math.BigInteger; -import java.time.ZoneId; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import static java.time.ZoneOffset.UTC; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; @@ -448,29 +448,28 @@ public class GeneralLiteralVisitorTest { @RunWith(Parameterized.class) public static class ValidDatetimeLiteralTest { - private static final ZoneId UTC = ZoneId.of("Z"); @Parameterized.Parameter(value = 0) public String script; @Parameterized.Parameter(value = 1) - public Date expected; + public OffsetDateTime expected; @Parameterized.Parameters(name = "{0}") public static Iterable<Object[]> generateTestParameters() { return Arrays.asList(new Object[][]{ - {"datetime('2018-03-22T00:35:44.741Z')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"datetime('2018-03-22T00:35:44.741-0000')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"datetime('2018-03-22T00:35:44.741+0000')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"datetime('2018-03-22T00:35:44.741-0300')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(-3)).toInstant())}, - {"datetime('2018-03-22T00:35:44.741+1600')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(16)).toInstant())}, - {"datetime('2018-03-22T00:35:44.741')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"datetime('2018-03-22T00:35:44Z')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 0, UTC).toInstant())}, - {"datetime('2018-03-22T00:35:44')", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 0, UTC).toInstant())}, - {"datetime('2018-03-22')", Date.from(ZonedDateTime.of(2018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"datetime('1018-03-22')", Date.from(ZonedDateTime.of(1018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"datetime('9018-03-22')", Date.from(ZonedDateTime.of(9018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"datetime('1000-001')", Date.from(ZonedDateTime.of(1000, 1, 1, 0, 0, 0, 0, UTC).toInstant())}, + {"datetime('2018-03-22T00:35:44.741Z')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"datetime('2018-03-22T00:35:44.741-0000')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"datetime('2018-03-22T00:35:44.741+0000')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"datetime('2018-03-22T00:35:44.741-0300')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(-3))}, + {"datetime('2018-03-22T00:35:44.741+1600')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(16))}, + {"datetime('2018-03-22T00:35:44.741')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"datetime('2018-03-22T00:35:44Z')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 0), UTC)}, + {"datetime('2018-03-22T00:35:44')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 0), UTC)}, + {"datetime('2018-03-22')", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 0, 0, 0, 0), UTC)}, + {"datetime('1018-03-22')", OffsetDateTime.of(LocalDateTime.of(1018, 03, 22, 0, 0, 0, 0), UTC)}, + {"datetime('9018-03-22')", OffsetDateTime.of(LocalDateTime.of(9018, 03, 22, 0, 0, 0, 0), UTC)}, + {"datetime('1000-001')", OffsetDateTime.of(LocalDateTime.of(1000, 1, 1, 0, 0, 0, 0), UTC)}, }); } @@ -480,7 +479,7 @@ public class GeneralLiteralVisitorTest { final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer)); final GremlinParser.DateLiteralContext ctx = parser.dateLiteral(); - final Date dt = (Date) new GenericLiteralVisitor(new GremlinAntlrToJava()).visitDateLiteral(ctx); + final OffsetDateTime dt = (OffsetDateTime) new GenericLiteralVisitor(new GremlinAntlrToJava()).visitDateLiteral(ctx); assertEquals(expected, dt); } } @@ -492,8 +491,8 @@ public class GeneralLiteralVisitorTest { final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer)); final GremlinParser.DateLiteralContext ctx = parser.dateLiteral(); - final Date dt = (Date) new GenericLiteralVisitor(new GremlinAntlrToJava()).visitDateLiteral(ctx); - assertTrue(new Date().getTime() - dt.getTime() < 1000); + final OffsetDateTime dt = (OffsetDateTime) new GenericLiteralVisitor(new GremlinAntlrToJava()).visitDateLiteral(ctx); + assertTrue(OffsetDateTime.now(UTC).toEpochSecond() - dt.toEpochSecond() < 1000); } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java index b16fbf057d..91955a13df 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java @@ -469,12 +469,12 @@ public class GremlinTranslatorTest { {"g.with('x', datetime('2023-08-02T00:00:00Z'))", null, "g.with(string0, date0)", - "g.With(\"x\", DateTimeOffset.FromUnixTimeMilliseconds(1690934400000))", - "g.With(\"x\", time.UnixMilli(1690934400000))", + "g.With(\"x\", DateTimeOffset.FromUnixTimeMilliseconds(1690934400000))", // todo: .net need update + "g.With(\"x\", time.UnixMilli(1690934400000))", // todo: go need update null, - "g.with(\"x\", new Date(1690934400000))", - "g.with_(\"x\", new Date(1690934400000))", - "g.with_('x', datetime.datetime.utcfromtimestamp(1690934400000 / 1000.0))"}, + "g.with(\"x\", OffsetDateTime.parse(\"2023-08-02T00:00Z\"))", + "g.with_(\"x\", new Date(1690934400000))", // todo: js need update + "g.with_('x', datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc))"}, {"g.with('x', [x: 1])", "g.with('x', [x:1])", "g.with(string0, map0)", diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java index 01e630fc82..9d17501923 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java @@ -52,7 +52,6 @@ public class OrderTest { @RunWith(Parameterized.class) public static class OrderListTest { - // todo: re-enable date-related tests after implementing datetime private static final SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.US); @Parameterized.Parameters(name = "{0}.sort({1}) = {2}") @@ -62,10 +61,10 @@ public class OrderTest { {Order.asc, Arrays.asList("b", "a", "c", "d"), Arrays.asList("a", "b", "c", "d")}, {Order.desc, Arrays.asList("b", "a", "c", "d"), Arrays.asList("d", "c", "b", "a")}, {Order.desc, Arrays.asList("c", "a", null, "d"), Arrays.asList("d", "c", "a", null)}, -// {Order.asc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), -// Arrays.asList(formatter.parse("1-Jan-2008"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"))}, -// {Order.desc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), -// Arrays.asList(formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2008"))}, + {Order.asc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), + Arrays.asList(formatter.parse("1-Jan-2008"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"))}, + {Order.desc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), + Arrays.asList(formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2008"))}, {Order.desc, Arrays.asList(100L, 1L, null, -1L, 0L), Arrays.asList(100L, 1L, 0L, -1L, null)}, {Order.desc, Arrays.asList(100L, 1L, -1L, 0L), Arrays.asList(100L, 1L, 0L, -1L)}, {Order.asc, Arrays.asList(100L, 1L, null, -1L, 0L), Arrays.asList(null, -1L, 0L, 1L, 100L)}, diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java index 4d6e7b6377..68214eed88 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java @@ -24,10 +24,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.junit.Test; import java.time.Instant; -import java.time.ZonedDateTime; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.UUID; @@ -44,13 +45,12 @@ public class AsDateStepTest extends StepTest { @Test public void shouldParseDate() { - final Instant testInstant = ZonedDateTime.of(2023, 8, 2, 0, 0, 0, 0, UTC).toInstant(); - final Date testDate = new Date(testInstant.getEpochSecond() * 1000); + final OffsetDateTime testDate = OffsetDateTime.of(LocalDateTime.of(2023, 8, 2, 0, 0, 0, 0), UTC); - assertEquals(new Date(1), __.__(1).asDate().next()); - assertEquals(new Date(2), __.__(2.0).asDate().next()); - assertEquals(new Date(3), __.__(3L).asDate().next()); - assertEquals(testDate, __.__(testDate.getTime()).asDate().next()); + assertEquals(OffsetDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneOffset.UTC), __.__(1).asDate().next()); + assertEquals(OffsetDateTime.ofInstant(Instant.ofEpochMilli(2), ZoneOffset.UTC), __.__(2.0).asDate().next()); + assertEquals(OffsetDateTime.ofInstant(Instant.ofEpochMilli(3), ZoneOffset.UTC), __.__(3L).asDate().next()); + assertEquals(testDate, __.__(testDate.toInstant().toEpochMilli()).asDate().next()); assertEquals(testDate, __.__("2023-08-02T00:00:00Z").asDate().next()); assertEquals(testDate, __.__(testDate).asDate().next()); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStepTest.java index f7bb2efbeb..135736722f 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStepTest.java @@ -24,11 +24,12 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.junit.Test; -import java.util.Calendar; +import java.time.Duration; +import java.time.OffsetDateTime; import java.util.Collections; -import java.util.Date; import java.util.List; +import static java.time.ZoneOffset.UTC; import static org.junit.Assert.assertEquals; public class DateAddStepTest extends StepTest { @@ -40,60 +41,40 @@ public class DateAddStepTest extends StepTest { @Test public void shouldAddHours() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.add(Calendar.HOUR_OF_DAY, 2); - final Date expected = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime expected = now.plus(Duration.ofHours(2)); assertEquals(expected, __.__(now).dateAdd(DT.hour, 2).next()); } @Test public void shouldAddNegativeHours() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.add(Calendar.HOUR_OF_DAY, -3); - final Date expected = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime expected = now.plus(Duration.ofHours(-3)); assertEquals(expected, __.__(now).dateAdd(DT.hour, -3).next()); } @Test public void shouldAddMinutes() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.add(Calendar.MINUTE, 5); - final Date expected = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime expected = now.plus(Duration.ofMinutes(5)); assertEquals(expected, __.__(now).dateAdd(DT.minute, 5).next()); } @Test public void shouldAddSeconds() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.add(Calendar.SECOND, 15); - final Date expected = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime expected = now.plus(Duration.ofSeconds(15)); assertEquals(expected, __.__(now).dateAdd(DT.second, 15).next()); } @Test public void shouldAddDays() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.add(Calendar.DAY_OF_MONTH, 50); - final Date expected = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime expected = now.plus(Duration.ofDays(50)); assertEquals(expected, __.__(now).dateAdd(DT.day, 50).next()); } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java index 0cffa601ba..8ff2effdee 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java @@ -23,11 +23,10 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.junit.Test; -import java.util.Calendar; +import java.time.Duration; +import java.time.OffsetDateTime; import java.util.Collections; -import java.util.Date; import java.util.List; -import java.util.TimeZone; import static java.time.ZoneOffset.UTC; import static org.junit.Assert.assertEquals; @@ -36,64 +35,49 @@ public class DateDiffStepTest extends StepTest { @Override protected List<Traversal> getTraversals() { - return Collections.singletonList(__.dateDiff(new Date(1690934420000L))); + return Collections.singletonList(__.dateDiff(OffsetDateTime.parse("2023-08-02T00:00:20Z"))); } @Test public void shouldHandlePositiveValues() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.setTimeZone(TimeZone.getTimeZone(UTC)); - cal.add(Calendar.DAY_OF_MONTH, 7); - final Date other = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime other = now.plus(Duration.ofDays(7)); assertEquals(604800L, (long) __.__(other).dateDiff(now).next()); } @Test public void shouldHandleNegativeValues() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.setTimeZone(TimeZone.getTimeZone(UTC)); - cal.add(Calendar.DAY_OF_MONTH, 7); - final Date other = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime other = now.plus(Duration.ofDays(7)); assertEquals(-604800L, (long) __.__(now).dateDiff(other).next()); } @Test public void shouldHandleTraversalParam() { - final Date now = new Date(); - - final Calendar cal = Calendar.getInstance(); - cal.setTime(now); - cal.setTimeZone(TimeZone.getTimeZone(UTC)); - cal.add(Calendar.DAY_OF_MONTH, 7); - final Date other = cal.getTime(); + final OffsetDateTime now = OffsetDateTime.now(UTC); + final OffsetDateTime other = now.plus(Duration.ofDays(7)); assertEquals(-604800L, (long) __.__(now).dateDiff(__.constant(other)).next()); } @Test public void shouldHandleNullTraversalParam() { - final Date now = new Date(); + final OffsetDateTime now = OffsetDateTime.now(UTC); - assertEquals(now.getTime() / 1000, (long) __.__(now).dateDiff(__.constant(null)).next()); + assertEquals(now.toEpochSecond(), (long) __.__(now).dateDiff(__.constant(null)).next()); } @Test public void shouldHandleNullDate() { - final Date now = new Date(); + final OffsetDateTime now = OffsetDateTime.now(UTC); - assertEquals(now.getTime() / 1000, (long) __.__(now).dateDiff((Date) null).next()); + assertEquals(now.toEpochSecond(), (long) __.__(now).dateDiff((OffsetDateTime) null).next()); } @Test(expected = IllegalArgumentException.class) public void shouldThrowWhenInputIsNotDate() { - __.__("2023-08-23T00:00:00Z").dateDiff(new Date()).next(); + __.__("2023-08-23T00:00:00Z").dateDiff(OffsetDateTime.now(UTC)).next(); } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/DatetimeHelperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/DatetimeHelperTest.java index 72ea3f1558..a3a1372903 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/DatetimeHelperTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/DatetimeHelperTest.java @@ -24,11 +24,11 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.time.Instant; -import java.time.ZoneId; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Arrays; -import java.util.Date; import static java.time.ZoneOffset.UTC; import static org.junit.Assert.assertEquals; @@ -43,35 +43,35 @@ public class DatetimeHelperTest { public String d; @Parameterized.Parameter(value = 1) - public Date expected; + public OffsetDateTime expected; @Parameterized.Parameters(name = "{0}") public static Iterable<Object[]> generateTestParameters() { return Arrays.asList(new Object[][]{ - {"2018-03-22T00:35:44.741Z", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741-0000", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741+0000", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741+00:00", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741+000000", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741+00:00:00", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44.741-0300", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(-3)).toInstant())}, - {"2018-03-22T00:35:44.741+1600", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(16)).toInstant())}, - {"2018-03-22T00:35:44.741+16:00", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(16)).toInstant())}, - {"2018-03-22T00:35:44.741+160000", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(16)).toInstant())}, - {"2018-03-22T00:35:44.741+16:00:00", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHours(16)).toInstant())}, - {"2018-03-22T00:35:44.741+1659", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutes(16, 59)).toInstant())}, - {"2018-03-22T00:35:44.741+16:59", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutes(16, 59)).toInstant())}, - {"2018-03-22T00:35:44.741+165900", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutes(16, 59)).toInstant())}, - {"2018-03-22T00:35:44.741+16:59:00", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutes(16, 59)).toInstant())}, - {"2018-03-22T00:35:44.741+165930", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutesSeconds(16, 59, 30)).toInstant())}, - {"2018-03-22T00:35:44.741+16:59:30", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, ZoneOffset.ofHoursMinutesSeconds(16, 59, 30)).toInstant())}, - {"2018-03-22T00:35:44.741", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 741000000, UTC).toInstant())}, - {"2018-03-22T00:35:44Z", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 0, UTC).toInstant())}, - {"2018-03-22T00:35:44", Date.from(ZonedDateTime.of(2018, 03, 22, 00, 35, 44, 0, UTC).toInstant())}, - {"2018-03-22", Date.from(ZonedDateTime.of(2018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"1018-03-22", Date.from(ZonedDateTime.of(1018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"9018-03-22", Date.from(ZonedDateTime.of(9018, 03, 22, 0, 0, 0, 0, UTC).toInstant())}, - {"1000-001", Date.from(ZonedDateTime.of(1000, 1, 1, 0, 0, 0, 0, UTC).toInstant())}, + {"2018-03-22T00:35:44.741Z", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741-0000", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741+0000", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741+00:00", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741+000000", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741+00:00:00", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44.741-0300", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(-3))}, + {"2018-03-22T00:35:44.741+1600", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(16))}, + {"2018-03-22T00:35:44.741+16:00", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(16))}, + {"2018-03-22T00:35:44.741+160000", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(16))}, + {"2018-03-22T00:35:44.741+16:00:00", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHours(16))}, + {"2018-03-22T00:35:44.741+1659", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutes(16, 59))}, + {"2018-03-22T00:35:44.741+16:59", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutes(16, 59))}, + {"2018-03-22T00:35:44.741+165900", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutes(16, 59))}, + {"2018-03-22T00:35:44.741+16:59:00", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutes(16, 59))}, + {"2018-03-22T00:35:44.741+165930", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutesSeconds(16, 59, 30))}, + {"2018-03-22T00:35:44.741+16:59:30", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), ZoneOffset.ofHoursMinutesSeconds(16, 59, 30))}, + {"2018-03-22T00:35:44.741", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 741000000), UTC)}, + {"2018-03-22T00:35:44Z", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 0), UTC)}, + {"2018-03-22T00:35:44", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 00, 35, 44, 0), UTC)}, + {"2018-03-22", OffsetDateTime.of(LocalDateTime.of(2018, 03, 22, 0, 0, 0, 0), UTC)}, + {"1018-03-22", OffsetDateTime.of(LocalDateTime.of(1018, 03, 22, 0, 0, 0, 0), UTC)}, + {"9018-03-22", OffsetDateTime.of(LocalDateTime.of(9018, 03, 22, 0, 0, 0, 0), UTC)}, + {"1000-001", OffsetDateTime.of(LocalDateTime.of(1000, 1, 1, 0, 0, 0, 0), UTC)}, }); } diff --git a/gremlin-python/src/main/python/gremlin_python/statics.py b/gremlin-python/src/main/python/gremlin_python/statics.py index fbd5422e3f..c3b9cd8d5b 100644 --- a/gremlin-python/src/main/python/gremlin_python/statics.py +++ b/gremlin-python/src/main/python/gremlin_python/statics.py @@ -45,14 +45,6 @@ SetType = set ByteBufferType = bytes -class timestamp(float): - """ - In Python a timestamp is simply a float. This dummy class (similar to long), allows users to wrap a float - in a GLV script to make sure the value is serialized as a Gremlin timestamp. - """ - pass - - class SingleByte(int): """ Provides a way to pass a single byte via Gremlin. diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py index 095d7debe4..3cf8f53a3e 100644 --- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py +++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py @@ -17,14 +17,13 @@ specific language governing permissions and limitations under the License. """ import calendar -import datetime import io import logging import math import struct import uuid from collections import OrderedDict -from datetime import timedelta +from datetime import datetime, timedelta, timezone from struct import pack, unpack from aenum import Enum @@ -48,7 +47,7 @@ class DataType(Enum): int = 0x01 long = 0x02 string = 0x03 - # date = 0x04 + datetime = 0x04 double = 0x07 float = 0x08 list = 0x09 @@ -74,7 +73,6 @@ class DataType(Enum): char = 0x80 duration = 0x81 marker = 0xfd - offsetdatetime = 0x88 # todo: will need to update to datetime custom = 0x00 # todo @@ -356,6 +354,45 @@ class BigDecimalIO(_GraphBinaryTypeIO): return cls.is_null(buff, reader, lambda b, r: cls._read(b), nullable) +class DateTimeIO(_GraphBinaryTypeIO): + + python_type = datetime + graphbinary_type = DataType.datetime + + @classmethod + def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True): + if obj.tzinfo is None: + raise AttributeError("Timezone information is required when constructing datetime") + cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend) + IntIO.dictify(obj.year, writer, to_extend, True, False) + ByteIO.dictify(obj.month, writer, to_extend, True, False) + ByteIO.dictify(obj.day, writer, to_extend, True, False) + # construct time of day in nanoseconds + h = obj.time().hour + m = obj.time().minute + s = obj.time().second + ms = obj.time().microsecond + ns = round((h*60*60*1e9) + (m*60*1e9) + (s*1e9) + (ms*1e3)) + LongIO.dictify(ns, writer, to_extend, True, False) + os = round(obj.utcoffset().total_seconds()) + IntIO.dictify(os, writer, to_extend, True, False) + return to_extend + + @classmethod + def objectify(cls, buff, reader, nullable=True): + return cls.is_null(buff, reader, cls._read_dt, nullable) + + @classmethod + def _read_dt(cls, b, r): + year = r.to_object(b, DataType.int, False) + month = r.to_object(b, DataType.byte, False) + day = r.to_object(b, DataType.byte, False) + ns = r.to_object(b, DataType.long, False) + offset = r.to_object(b, DataType.int, False) + tz = timezone(timedelta(seconds=offset)) + return datetime(year, month, day, tzinfo=tz) + timedelta(microseconds=ns/1000) + + class CharIO(_GraphBinaryTypeIO): python_type = SingleChar graphbinary_type = DataType.char diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py index 53bdd0e240..6ba00d7d78 100644 --- a/gremlin-python/src/main/python/radish/feature_steps.py +++ b/gremlin-python/src/main/python/radish/feature_steps.py @@ -231,7 +231,7 @@ def _convert(val, ctx): return val[4:-1] elif isinstance(val, str) and re.match(r"^dt\[.*\]$", val): # parse datetime # python 3.8 can handle only subset of ISO 8601 dates - return datetime.fromisoformat(val[3:-1].replace('Z', '')) + return datetime.fromisoformat(val[3:-1].replace('Z', '+00:00')) elif isinstance(val, str) and re.match(r"^d\[NaN\]$", val): # parse nan return float("nan") elif isinstance(val, str) and re.match(r"^d\[Infinity\]$", val): # parse +inf diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 448717fee9..04ef5e0e6b 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -522,6 +522,13 @@ world.gremlins = { 'g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX': [(lambda g:g.add_v('person').property('name', 'marko').property('friendWeight', None, 'acl', None)), (lambda g:g.V().has('person', 'name', 'marko').has('friendWeight', None)), (lambda g:g.V().has('person', 'name', 'marko').properties('friendWeight').has('acl', None)), (lambda g:g.V().has('person', 'name', 'marko').properties('friendWeight').count())], 'g_V_hasXperson_name_aliceX_propertyXsingle_age_unionXage_constantX1XX_sumX': [(lambda g:g.add_v('person').property('name', 'alice').property(Cardinality.single, 'age', 50)), (lambda g:g.V().has('person', 'name', 'alice').property('age', __.union(__.values('age'), __.constant(1)).sum_())), (lambda g:g.V().has('person', 'age', 50)), (lambda g:g.V().has('person', 'age', 51))], 'g_V_limitX3X_addVXsoftwareX_aggregateXa1X_byXlabelX_aggregateXa2X_byXlabelX_capXa1_a2X_selectXa_bX_byXunfoldX_foldX': [(lambda g:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property(' [...] + 'g_injectXstrX_asDate': [(lambda g:g.inject('2023-08-02T00:00:00Z').as_date())], + 'g_injectX1694017707000X_asDate': [(lambda g:g.inject(long(1694017707000)).as_date())], + 'g_injectX1694017708000LX_asDate': [(lambda g, xx1=None:g.inject(xx1).as_date())], + 'g_injectX1694017709000dX_asDate': [(lambda g, xx1=None:g.inject(xx1).as_date())], + 'g_injectX1_2X_asDate': [(lambda g, xx1=None:g.inject(xx1).as_date())], + 'g_injectXnullX_asDate': [(lambda g:g.inject(None).as_date())], + 'g_injectXinvalidstrX_asDate': [(lambda g:g.inject('This String is not an ISO 8601 Date').as_date())], 'g_injectX1_2X_asString': [(lambda g, xx1=None,xx2=None:g.inject(xx1, xx2).as_string())], 'g_injectX1_2X_asStringXlocalX': [(lambda g, xx1=None,xx2=None:g.inject(xx1, xx2).as_string(Scope.local))], 'g_injectXlist_1_2X_asStringXlocalX': [(lambda g, xx1=None:g.inject(xx1).as_string(Scope.local))], @@ -628,9 +635,15 @@ world.gremlins = { 'g_V_order_byXnoX_count': [(lambda g:g.V().order().by('no').count())], 'g_V_group_byXlabelX_count': [(lambda g:g.V().group().by(T.label).count())], 'g_V_group_byXlabelX_countXlocalX': [(lambda g:g.V().group().by(T.label).count(Scope.local))], - 'g_injectXdatetimeXstr1XX_dateDiffXdatetimeXstr2XX': [(lambda g:g.inject(datetime.datetime.utcfromtimestamp(1690934400000 / 1000.0)).date_diff(datetime.datetime.utcfromtimestamp(1691539200000 / 1000.0)))], - 'g_injectXdatetimeXstr1XX_dateDiffXconstantXdatetimeXstr2XXX': [(lambda g:g.inject(datetime.datetime.utcfromtimestamp(1691452800000 / 1000.0)).date_diff(__.constant(datetime.datetime.utcfromtimestamp(1690848000000 / 1000.0))))], - 'g_injectXdatetimeXstr1XX_dateDiffXinjectXdatetimeXstr2XXX': [(lambda g:g.inject(datetime.datetime.utcfromtimestamp(1691452800000 / 1000.0)).date_diff(__.inject(datetime.datetime.utcfromtimestamp(1696982400000 / 1000.0))))], + 'g_injectXdatetimeXstrXX_dateAddXDT_hour_2X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_add(DT.hour, 2))], + 'g_injectXdatetimeXstrXX_dateAddXhour_2X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_add(DT.hour, 2))], + 'g_injectXdatetimeXstrXX_dateAddXhour_1X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_add(DT.hour, -1))], + 'g_injectXdatetimeXstrXX_dateAddXminute_10X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_add(DT.minute, 10))], + 'g_injectXdatetimeXstrXX_dateAddXsecond_20X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_add(DT.second, 20))], + 'g_injectXdatetimeXstrXX_dateAddXday_11X': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1693958400).astimezone(datetime.timezone.utc)).date_add(DT.day, 11))], + 'g_injectXdatetimeXstr1XX_dateDiffXdatetimeXstr2XX': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1690934400).astimezone(datetime.timezone.utc)).date_diff(datetime.datetime.fromtimestamp(1691539200).astimezone(datetime.timezone.utc)))], + 'g_injectXdatetimeXstr1XX_dateDiffXconstantXdatetimeXstr2XXX': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1691452800).astimezone(datetime.timezone.utc)).date_diff(__.constant(datetime.datetime.fromtimestamp(1690848000).astimezone(datetime.timezone.utc))))], + 'g_injectXdatetimeXstr1XX_dateDiffXinjectXdatetimeXstr2XXX': [(lambda g:g.inject(datetime.datetime.fromtimestamp(1691452800).astimezone(datetime.timezone.utc)).date_diff(__.inject(datetime.datetime.fromtimestamp(1696982400).astimezone(datetime.timezone.utc))))], 'g_injectXnullX_differenceXinjectX1XX': [(lambda g:g.inject(None).difference(__.inject(1)))], 'g_V_valuesXnameX_differenceXV_foldX': [(lambda g:g.V().values('name').difference(__.V().fold()))], 'g_V_fold_differenceXconstantXnullXX': [(lambda g:g.V().fold().difference(__.constant(None)))], diff --git a/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py b/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py index 5f8c2de196..8dbd0a5af5 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py @@ -17,10 +17,10 @@ specific language governing permissions and limitations under the License. ''' -import datetime import uuid - import pytest + +from datetime import datetime, timedelta, timezone from gremlin_python.driver.serializer import GraphBinarySerializersV4 from gremlin_python.process.anonymous_traversal import traversal from gremlin_python.statics import * @@ -81,30 +81,16 @@ def test_vertex_vertex_properties(remote_connection_crew): assert vertex.properties[1].properties[1].value == 2000 [email protected](reason="timestamp replaced by datetime in GraphBinaryV4, revisit to update after implementation") -def test_timestamp(remote_connection): - g = traversal().with_(remote_connection) - ts = timestamp(1481750076295 / 1000) - resp = g.add_v('test_vertex').property('ts', ts) - resp = resp.to_list() - vid = resp[0].id - try: - ts_prop = g.V(vid).properties('ts').to_list()[0] - assert isinstance(ts_prop.value, timestamp) - assert ts_prop.value == ts - finally: - g.V(vid).drop().iterate() - - [email protected](reason="timestamp replaced by datetime in GraphBinaryV4, revisit to update after implementation") def test_datetime(remote_connection): g = traversal().with_(remote_connection) - dt = datetime.datetime.utcfromtimestamp(1481750076295 / 1000) + tz = timezone(timedelta(seconds=36000)) + ms = 12345678912 + dt = datetime(2022, 5, 20, tzinfo=tz) + timedelta(microseconds=ms) resp = g.add_v('test_vertex').property('dt', dt).to_list() vid = resp[0].id try: dt_prop = g.V(vid).properties('dt').to_list()[0] - assert isinstance(dt_prop.value, datetime.datetime) + assert isinstance(dt_prop.value, datetime) assert dt_prop.value == dt finally: g.V(vid).drop().iterate() @@ -209,7 +195,7 @@ def test_odd_bits(remote_connection): finally: g.V(vid).drop().iterate() - dur = datetime.timedelta(seconds=1000, microseconds=1000) + dur = timedelta(seconds=1000, microseconds=1000) resp = g.add_v('test_vertex').property('dur', dur).to_list() vid = resp[0].id try: diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV4.py b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV4.py index 0702895ac4..486773fa0d 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV4.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV4.py @@ -17,11 +17,11 @@ specific language governing permissions and limitations under the License. """ -import datetime import uuid import math -from gremlin_python.statics import timestamp, long, bigint, BigDecimal, SingleByte, SingleChar, ByteBufferType +from datetime import datetime, timedelta, timezone +from gremlin_python.statics import long, bigint, BigDecimal, SingleByte, SingleChar, ByteBufferType from gremlin_python.structure.graph import Vertex, Edge, Property, VertexProperty, Path from gremlin_python.structure.io.graphbinaryV4 import GraphBinaryWriter, GraphBinaryReader from gremlin_python.process.traversal import Direction @@ -79,8 +79,25 @@ class TestGraphBinaryV4(object): assert x.scale == output.scale assert x.unscaled_value == output.unscaled_value - def test_timestamp(self): - x = timestamp(1481750076295 / 1000) + def test_datetime(self): + tz = timezone(timedelta(seconds=36000)) + ms = 12345678912 + x = datetime(2022, 5, 20, tzinfo=tz) + timedelta(microseconds=ms) + output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) + assert x == output + + def test_datetime_format(self): + x = datetime.strptime('2022-05-20T03:25:45.678912Z', '%Y-%m-%dT%H:%M:%S.%f%z') + output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) + assert x == output + + def test_datetime_local(self): + x = datetime.now().astimezone() + output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) + assert x == output + + def test_datetime_epoch(self): + x = datetime.fromtimestamp(1690934400).astimezone(timezone.utc) output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) assert x == output @@ -202,7 +219,7 @@ class TestGraphBinaryV4(object): assert x == output def test_duration(self): - x = datetime.timedelta(seconds=1000, microseconds=1000) + x = timedelta(seconds=1000, microseconds=1000) output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) assert x == output diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsDate.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsDate.feature index 122be0dff7..23f46ee70c 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsDate.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsDate.feature @@ -18,84 +18,84 @@ # todo: re-enable after datetime is implemented @StepClassMap @StepAsDate Feature: Step - asDate() -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXstrX_asDate -# Given the empty graph -# And the traversal of -# """ -# g.inject("2023-08-02T00:00:00Z").asDate() -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-02T00:00:00Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectX1694017707000X_asDate -# Given the empty graph -# And the traversal of -# """ -# g.inject(1694017707000).asDate() -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-09-06T16:28:27Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectX1694017708000LX_asDate -# Given the empty graph -# And using the parameter xx1 defined as "d[1694017708000].l" -# And the traversal of -# """ -# g.inject(xx1).asDate() -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-09-06T16:28:28Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectX1694017709000dX_asDate -# Given the empty graph -# And using the parameter xx1 defined as "d[1694017709000.1].d" -# And the traversal of -# """ -# g.inject(xx1).asDate() -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-09-06T16:28:29Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectX1_2X_asDate -# Given the empty graph -# And using the parameter xx1 defined as "l[1,2]" -# And the traversal of -# """ -# g.inject(xx1).asDate() -# """ -# When iterated to list -# Then the traversal will raise an error with message containing text of "Can't parse" -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXnullX_asDate -# Given the empty graph -# And the traversal of -# """ -# g.inject(null).asDate() -# """ -# When iterated to list -# Then the traversal will raise an error with message containing text of "Can't parse" -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXinvalidstrX_asDate -# Given the empty graph -# And the traversal of -# """ -# g.inject('This String is not an ISO 8601 Date').asDate() -# """ -# When iterated to list -# Then the traversal will raise an error with message containing text of "Can't parse" \ No newline at end of file + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXstrX_asDate + Given the empty graph + And the traversal of + """ + g.inject("2023-08-02T00:00:00Z").asDate() + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-02T00:00:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1694017707000X_asDate + Given the empty graph + And the traversal of + """ + g.inject(1694017707000).asDate() + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-09-06T16:28:27Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1694017708000LX_asDate + Given the empty graph + And using the parameter xx1 defined as "d[1694017708000].l" + And the traversal of + """ + g.inject(xx1).asDate() + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-09-06T16:28:28Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1694017709000dX_asDate + Given the empty graph + And using the parameter xx1 defined as "d[1694017709000.1].d" + And the traversal of + """ + g.inject(xx1).asDate() + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-09-06T16:28:29Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1_2X_asDate + Given the empty graph + And using the parameter xx1 defined as "l[1,2]" + And the traversal of + """ + g.inject(xx1).asDate() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "Can't parse" + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXnullX_asDate + Given the empty graph + And the traversal of + """ + g.inject(null).asDate() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "Can't parse" + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXinvalidstrX_asDate + Given the empty graph + And the traversal of + """ + g.inject('This String is not an ISO 8601 Date').asDate() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "Can't parse" \ No newline at end of file diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/DateAdd.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/DateAdd.feature index c22dae07f2..939c7419bf 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/DateAdd.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/DateAdd.feature @@ -18,75 +18,75 @@ # todo: re-enable after datetime is implemented @StepClassMap @StepDateAdd Feature: Step - dateAdd() -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXDT_hour_2X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(DT.hour, 2) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-02T02:00:00Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXhour_2X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(hour, 2) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-02T02:00:00Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXhour_1X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(hour, -1) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-01T23:00:00Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXminute_10X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(minute, 10) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-02T00:10:00Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXsecond_20X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(second, 20) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-08-02T00:00:20Z] | -# -# @GraphComputerVerificationInjectionNotSupported -# Scenario: g_injectXdatetimeXstrXX_dateAddXday_11X -# Given the empty graph -# And the traversal of -# """ -# g.inject(datetime('2023-09-06T00:00:00Z')).dateAdd(day, 11) -# """ -# When iterated to list -# Then the result should be unordered -# | result | -# | dt[2023-09-17T00:00:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXDT_hour_2X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(DT.hour, 2) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-02T02:00:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXhour_2X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(hour, 2) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-02T02:00:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXhour_1X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(hour, -1) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-01T23:00:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXminute_10X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(minute, 10) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-02T00:10:00Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXsecond_20X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-08-02T00:00:00Z')).dateAdd(second, 20) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-08-02T00:00:20Z] | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXdatetimeXstrXX_dateAddXday_11X + Given the empty graph + And the traversal of + """ + g.inject(datetime('2023-09-06T00:00:00Z')).dateAdd(day, 11) + """ + When iterated to list + Then the result should be unordered + | result | + | dt[2023-09-17T00:00:00Z] | diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePerson.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePerson.java index 2970dce3bb..1e1f238038 100644 --- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePerson.java +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePerson.java @@ -18,7 +18,7 @@ */ package org.apache.tinkerpop.gremlin.util.ser.binary.types.sample; -import java.util.Date; +import java.time.OffsetDateTime; import java.util.Objects; /** @@ -26,9 +26,9 @@ import java.util.Objects; */ public class SamplePerson { private final String name; - private final Date birthDate; + private final OffsetDateTime birthDate; - SamplePerson(final String name, final Date birthDate) { + SamplePerson(final String name, final OffsetDateTime birthDate) { Objects.requireNonNull(name); Objects.requireNonNull(birthDate); @@ -40,7 +40,7 @@ public class SamplePerson { return name; } - public Date getBirthDate() { + public OffsetDateTime getBirthDate() { return birthDate; } } diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializer.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializer.java index 2a76206c72..a030ed46d7 100644 --- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializer.java +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializer.java @@ -27,7 +27,7 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.types.CustomTypeSerializ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Date; +import java.time.OffsetDateTime; /** * A sample custom type serializer. @@ -78,7 +78,7 @@ public final class SamplePersonSerializer implements CustomTypeSerializer<Sample } final String name = context.readValue(buffer, String.class, false); - final Date birthDate = context.readValue(buffer, Date.class, false); + final OffsetDateTime birthDate = context.readValue(buffer, OffsetDateTime.class, false); return new SamplePerson(name, birthDate); } diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializerTest.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializerTest.java index 1f4924efd1..d8b9d02060 100644 --- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializerTest.java +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/sample/SamplePersonSerializerTest.java @@ -35,9 +35,9 @@ import org.junit.Test; import java.io.IOException; import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -46,8 +46,6 @@ import static org.apache.tinkerpop.gremlin.util.ser.AbstractMessageSerializer.TO import static org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4.TOKEN_CUSTOM; import static org.hamcrest.MatcherAssert.assertThat; -// todo: person contains removed date type, revisit after datetime is properly implemented -@Ignore public class SamplePersonSerializerTest { private static final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT; @@ -89,7 +87,7 @@ public class SamplePersonSerializerTest { final GraphBinaryWriter writer = new GraphBinaryWriter(registry); final SamplePerson person = new SamplePerson("Matias", - Date.from(LocalDateTime.of(2005, 8, 5, 1, 0).toInstant(ZoneOffset.UTC))); + OffsetDateTime.of(LocalDateTime.of(2005, 8, 5, 1, 0), ZoneOffset.UTC)); for (boolean nullable: new boolean[] { true, false }) { final Buffer buffer = bufferFactory.create(allocator.buffer()); @@ -102,7 +100,7 @@ public class SamplePersonSerializerTest { } private void assertPerson(final GraphBinaryMessageSerializerV4 serializer) throws IOException { - final Date birthDate = Date.from(LocalDateTime.of(2010, 4, 29, 5, 30).toInstant(ZoneOffset.UTC)); + final OffsetDateTime birthDate = OffsetDateTime.of(LocalDateTime.of(2010, 4, 29, 5, 30), ZoneOffset.UTC); final SamplePerson person = new SamplePerson("Olivia", birthDate); final ByteBuf serialized = serializer.serializeResponseAsBinary(
