如何将Java的ZonedDateTime存入Oracle的TIMESTAMPTZ类型字段?
问题原因
- 直接传入字符串存储时抛出
not a valid month错误,是因为Oracle会话默认的NLS_TIMESTAMP_TZ_FORMAT参数和你传入的时间字符串格式不匹配,Oracle无法完成自动解析。 - 尝试构造
TIMESTAMPTZ对象时抛出格式错误,是因为oracle.sql.TIMESTAMPTZ的字符串构造方法默认要求传入不带时区信息的时间字符串,会内部调用java.sql.Timestamp.valueOf做转换,而java.sql.Timestamp本身不支持携带时区偏移,你传入的带+02:00的字符串不符合它的格式要求,所以触发异常。
可行解决方案
方案一:直接使用ZonedDateTime存储(最推荐)
只要你使用的是ojdbc8及以上版本的Oracle驱动,原生支持Java 8+的java.time时间类型,可以直接映射到Oracle的TIMESTAMP WITH TIME ZONE字段,不需要依赖Oracle专有API:
import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; // 解析时间字符串为ZonedDateTime对象 String timeStr = "2021-10-28 02:36:08.000000 +02:00"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS XXX"); ZonedDateTime zonedDateTime = ZonedDateTime.parse(timeStr, formatter); // 直接设置参数即可,驱动会自动完成类型映射 PreparedStatement ps = con.prepareStatement(query); ps.setObject(1, zonedDateTime);
方案二:SQL层面指定格式解析
如果不想修改Java层面的时间处理逻辑,可以在SQL语句中用TO_TIMESTAMP_TZ函数指定格式,直接传入字符串参数:
- 编写SQL时指定时间格式:
INSERT INTO 你的表名 (时间字段名) VALUES (TO_TIMESTAMP_TZ(?, 'yyyy-mm-dd hh24:mi:ss.ff6 tzh:tzm'))
- Java代码直接传入字符串即可:
String d = "2021-10-28 02:36:08.000000 +02:00"; PreparedStatement ps = con.prepareStatement(query); ps.setString(1, d);
注意事项
- ojdbc版本需要匹配你使用的Oracle数据库版本,优先使用ojdbc8及以上版本,避免时间类型映射异常
- 格式符要和时间字符串完全匹配:如果你的时间字符串是6位小数秒就写
ff6,3位就写ff3,时区格式为±HH:MI就用tzh:tzm格式符 - 不要使用
java.sql.Timestamp做中转,该类不携带时区信息,转换过程中极易出现时区偏移错误
内容的提问来源于stack exchange,提问作者Pravin Mangalam
相关产品推荐
相关产品推荐

