如何在Apache TinkerPop Gremlin(AWS Neptune)中使用Java OffsetDateTime?
问题描述
我的应用支持多时区访问,因此尝试用Java的java.time.OffsetDateTime类存储日期时间。应用/API接收的时间格式如下:
startTime: "1996-12-19T16:39:57-08:00", endTime: "1996-12-19T16:39:57-08:00"
但用Apache Gremlin将数据存入AWS Neptune时,抛出以下错误:
org.apache.tinkerpop.gremlin.driver.exception.ResponseException: {"detailedMessage":"Unsupported property value type: java.time.OffsetDateTime","code":"UnsupportedOperationException","requestId":"81e402b0-90ab-4c6d-8839-23542da3437b"} at org.apache.tinkerpop.gremlin.driver.Handler$GremlinResponseHandler.channelRead0(Handler.java:245) ~[gremlin-driver-3.6.1.jar:3.6.1] at org.apache.tinkerpop.gremlin.driver.Handler$GremlinResponseHandler.channelRead0(Handler.java:200) ~[gremlin-driver-3.6.1.jar:3.6.1] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at org.apache.tinkerpop.gremlin.driver.Handler$GremlinSaslAuthenticationHandler.channelRead0(Handler.java:126) ~[gremlin-driver-3.6.1.jar:3.6.1] at org.apache.tinkerpop.gremlin.driver.Handler$GremlinSaslAuthenticationHandler.channelRead0(Handler.java:68) ~[gremlin-driver-3.6.1.jar:3.6.1] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.79.Final.jar:4.1.79.Final]
请问Gremlin/Neptune是否不支持OffsetDateTime?该如何高效存储和检索此类带时区偏移的日期时间?
解决方案
1. OffsetDateTime支持情况说明
AWS Neptune和Apache Gremlin不直接支持java.time.OffsetDateTime类型。Neptune兼容Gremlin标准日期时间类型,主要支持java.util.Date、java.sql.Timestamp,以及符合ISO 8601格式的字符串(含时区偏移的格式也支持)。
2. 高效存储与检索方案
方案一:转为ISO 8601字符串存储
这是适配现有输入格式最直接的方式:
- 存储:调用
OffsetDateTime.toString()将对象转为标准ISO 8601带偏移格式的字符串,直接存入Neptune属性。 - 检索:读取字符串后用
OffsetDateTime.parse(str)转回对象;也可直接基于字符串做时间范围查询——Neptune支持ISO 8601字符串的字典序比较,其顺序与时间顺序一致。
示例代码:
// 存储转换 OffsetDateTime startTime = OffsetDateTime.parse("1996-12-19T16:39:57-08:00"); String startTimeStr = startTime.toString(); g.addV("Event").property("startTime", startTimeStr).next(); // 检索转换 Result result = g.V().hasLabel("Event").values("startTime").next(); OffsetDateTime retrievedStartTime = OffsetDateTime.parse(result.getString()); // 时间范围查询 OffsetDateTime timeStart = OffsetDateTime.parse("1996-12-19T00:00:00-08:00"); OffsetDateTime timeEnd = OffsetDateTime.parse("1996-12-20T00:00:00-08:00"); List<Vertex> events = g.V().hasLabel("Event") .has("startTime", gte(timeStart.toString())) .has("startTime", lte(timeEnd.toString())) .toList();
方案二:转为UTC时间戳存储
若需更高效的数值比较,可将OffsetDateTime转为UTC毫秒级时间戳(long类型):
- 存储:调用
offsetDateTime.toInstant().toEpochMilli()获取时间戳存入Neptune。 - 检索:读取时间戳后,用
Instant.ofEpochMilli(timestamp).atOffset(ZoneOffset.UTC)(或指定原偏移)转回OffsetDateTime。 - 优势:数值比较性能优于字符串,适合大规模时间范围查询场景。
示例代码:
// 存储转换 OffsetDateTime startTime = OffsetDateTime.parse("1996-12-19T16:39:57-08:00"); long startTimeTs = startTime.toInstant().toEpochMilli(); g.addV("Event").property("startTimeTs", startTimeTs).next(); // 检索转换 Result result = g.V().hasLabel("Event").values("startTimeTs").next(); OffsetDateTime retrievedStartTime = Instant.ofEpochMilli(result.getLong()) .atOffset(startTime.getOffset()); // 时间范围查询 long tsStart = OffsetDateTime.parse("1996-12-19T00:00:00-08:00").toInstant().toEpochMilli(); long tsEnd = OffsetDateTime.parse("1996-12-20T00:00:00-08:00").toInstant().toEpochMilli(); List<Vertex> events = g.V().hasLabel("Event") .has("startTimeTs", gte(tsStart)) .has("startTimeTs", lte(tsEnd)) .toList();
方案三:拆分LocalDateTime与偏移量(不推荐)
若需单独存储时区偏移,可将OffsetDateTime拆分为LocalDateTime和偏移量(如ZoneOffset的字符串/数值)分别存储。但此方式会增加查询复杂度,需同时比较两个属性,仅适用于特殊业务需求。
3. 注意事项
- 无论选择哪种方案,必须保持存储格式一致,避免混合不同格式的时间数据。
- 使用字符串存储时,务必保持标准ISO 8601格式(如
yyyy-MM-dd'T'HH:mm:ssXXX),确保Neptune能正确进行时间顺序比较。
内容的提问来源于stack exchange,提问作者Thirumal
相关产品推荐
相关产品推荐

