关于该主题的某些部分,已经说了很多(并写在 SO 上),但不是以全面、完整的方式,所以我们可以有一个“终极的、涵盖一切的”解决方案供每个人使用。
我有一个 Oracle DB,我在其中存储全局事件的日期+时间+时区,因此必须保留原始 TZ,并根据请求交付给客户端。理想情况下,它可以通过使用标准 ISO 8601“T”格式很好地工作,该格式可以使用“TIMESTAMP WITH TIME ZONE”列类型(“TSTZ”)很好地存储在 Oracle 中。
像'2013-01-02T03:04:05.060708+09:00'
我需要做的就是从数据库中检索上述值并将其发送给客户端,无需任何操作。
问题是 Java 缺乏对 ISO 8601(或任何其他 date+time+nano+tz 数据类型)的支持,情况更糟,因为 Oracle JDBC 驱动程序 (ojdbc6.jar) 对 TSTZ 的支持更少(相对于Oracle DB 本身得到很好的支持)。
具体来说,这是我不应该或不能做的事情:
- 任何从 TSTZ 到 java 日期、时间、时间戳的映射(例如通过 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 , ETC。)。此外,这种格式在 Oracle JDBC 驱动程序实现中是硬编码的(!),它也忽略了 JVM 语言环境设置和 Oracle 会话格式化设置(即它忽略了 NLS_TIMESTAMP_TZ_FORMAT 会话变量)。 - JDBC getObject() 或 getTIMESTAMPTZ() 都返回 Oracle 的 TIMESTAMPTZ 对象,这实际上是没有用的,因为它没有任何转换为 Calendar(只有 Date、Time 和 Timestamp),所以我们再次丢失了 TZ 信息。
所以,这是我剩下的选项:
使用 JDBC getString() 并对其进行字符串操作以修复并使 ISO 8601 兼容。这很容易做到,但如果 Oracle 更改内部硬编码的 getString() 格式,就会有死亡的危险。此外,通过查看 getString() 源代码,似乎使用 getString() 也会导致一些性能损失。
使用 Oracle DB“toString”转换:“SELECT TO_CHAR(tstz...) EVENT_TIME ...”。这很好用,但有两个主要缺点:
- 现在每个 SELECT 都必须包含 TO_CHAR 调用,这令人头疼,难以记住和编写
- 现在,每个 SELECT 都必须添加 EVENT_TIME 列“别名”(例如需要自动将结果序列化为 Json)
。
使用 Oracle 的 TIMESTAMPTZ java 类并从其内部(记录的)字节数组结构中手动提取相关值(即实现我自己的 toString() 方法,Oracle 忘记在那里实现)。如果 Oracle 改变内部结构(不太可能)并且需要相对复杂的功能来实现和维护,这是有风险的。
我希望有第 4 个很好的选择,但是从整个网络上看,所以 - 我看不到任何东西。
想法?意见?
更新
下面给出了很多想法,但看起来没有合适的方法来做到这一点。就个人而言,我认为使用方法#1是最短且最易读的方法(并且保持了不错的性能,而不会丢失亚毫秒或基于 SQL 时间的查询能力)。
这是我最终决定使用的:
String iso = rs.getString(col).replaceFirst(" ", "T");
谢谢大家的好答案,
B。