Oracle/JDBC:以ISO 8601格式检索TIMESTAMP WITH TIME ZONE值

Bor*_*rka 15 java database timezone datetime jdbc

关于部分内容已经说了很多(并且写在SO上),但不是全面,完整的方式,所以我们可以有一个"终极,覆盖所有"的解决方案供所有人使用.

我有一个Oracle DB,我存储全局事件的日期+时间+时区,因此必须保留原始TZ,并根据请求传送到客户端.理想情况下,它可以很好地使用标准的ISO 8601"T"格式,可以使用"TIMESTAMP WITH TIME ZONE"列类型("TSTZ")很好地存储在Oracle中.

"2013-01-02T03:04:05.060708 + 09:00"这样的东西

我需要做的就是从DB中检索上述值并将其发送到客户端而无需任何操作.

问题是Java缺乏ISO 8601(或任何其他日期+时间+ nano + tz数据类型)的支持,情况更糟,因为Oracle JDBC驱动程序(ojdbc6.jar)对TSTZ的支持更少(相对于Oracle DB本身得到了很好的支持).

具体来说,这是我不应该或不能做的:

  • 从TSTZ到Java Date,Time,Timestamp(例如通过JDBC getTimestamp()调用)的任何映射都不起作用,因为我丢失了TZ.
  • Oracle JDBC驱动程序没有提供将TSTZ映射到Java Calendar对象的任何方法(这可能是一个解决方案,但它不存在)
  • JDBC getString()可以工作,但是Oracle JDBC驱动程序返回格式为
    '2013-01-02 03:04:05.060708 +9:00'的字符串,这不符合ISO 8601(没有"T",TZ没有尾随0)等).此外,此格式在Oracle JDBC驱动程序实现中是硬编码的(!),它还忽略了JVM区域设置和Oracle会话格式设置(即它忽略了NLS_TIMESTAMP_TZ_FORMAT会话变量).
  • JDBC getObject()或getTIMESTAMPTZ()都返回Oracle的TIMESTAMPTZ对象,这实际上没用,因为它没有任何转换到Calendar(只有Date,Time和Timestamp),所以我们再次丢失TZ信息.

那么,这是我留下的选项:

  1. 使用JDBC getString(),并对其进行字符串操作以修复并使ISO 8601兼容.这很容易做到,但如果Oracle更改内部硬编码的getString()格式,则存在死亡的危险.另外,通过查看getString()源代码,似乎使用getString()也会导致一些性能损失.

  2. 使用Oracle DB"toString"转换:"SELECT TO_CHAR(tstz ...)EVENT_TIME ...".这很好,但有两个主要的不利因素:

    • 现在每个SELECT都必须包含TO_CHAR调用,这是一个令人头疼的记忆和写入
    • 现在每个SELECT都必须添加EVENT_TIME列"别名"(例如,需要自动将结果序列化为Json)
      .
  3. 使用Oracle的TIMESTAMPTZ java类并从其内部(记录的)字节数组结构中手动提取相关值(即实现我自己的toString()方法,Oracle忘记在那里实现).如果Oracle改变内部结构(不太可能)并且需要相对复杂的功能来实现和维护,这是有风险的.

  4. 我希望有第四个,很棒的选择,但是从整个网络上看也是如此 - 我看不到任何东西.

想法?意见?

UPDATE

下面给出了很多想法,但看起来没有正确的方法来做到这一点.就个人而言,我认为使用方法#1是最短且最易读的方式(并保持良好的性能,而不会丢失亚毫秒或基于SQL时间的查询功能).

这是我最终决定使用的:

String iso = rs.getString(col).replaceFirst(" ", "T");
Run Code Online (Sandbox Code Playgroud)

谢谢大家好的答案,
B.

Phi*_*all 5

JDBC getObject() 或 getTIMESTAMPTZ() 都返回 Oracle 的 TIMESTAMPTZ 对象,这实际上是无用的,因为它没有任何转换为​​ Calendar(只有日期、时间和时间戳),因此我们再次丢失了 TZ 信息。

这将是我的建议,因为这是获取所需信息的唯一可靠方法。

如果您使用的是 Java SE 8 并且有 ojdbc8,那么您可以使用getObject(int, OffsetDateTime.class)。请注意,当您使用getObject(int, ZonedDateTime.class) 时,您可能会受到错误 25792016 的影响。

使用 Oracle 的 TIMESTAMPTZ java 类并从其内部(记录在案的)字节数组结构中手动提取相关值(即实现我自己的 toString() 方法,Oracle 忘记在那里实现)。如果 Oracle 改变内部结构(不太可能)并且需要相对复杂的功能来实现和维护,这是有风险的。

这就是我们最终采用的方法,直到 Oracle JDBC 驱动程序中提供无错误 JSR-310 支持。我们确定这是获取我们想要的信息的唯一可靠方式。


Bor*_*rka 0

因为看起来没有神奇的方法可以正确地做到这一点,所以最简单和最短的方法是#1。具体来说,这是所需的所有代码:

// convert Oracle's hard-coded: '2013-01-02 03:04:05.060708 +9:00'
// to properly formatted ISO 8601: '2013-01-02T03:04:05.060708 +9:00'
String iso = rs.getString(col).replaceFirst(" ", "T"); 
Run Code Online (Sandbox Code Playgroud)

似乎只添加 'T' 就足够了,尽管完美主义者可能会添加更多化妆品(当然,正则表达式可以优化),例如: rs.getString(col).replaceFirst(" ", "T").replaceAll(" ", "").replaceFirst("\+([0-9])\:", "+0$1:");

B.