You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:06:19