假设我们有以下代码创建日期:
SimpleDateFormat sdf = new SimpleDateFormat( "dd/MM/yyyy" ); sdf.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); // we know the date being parsed is UTC Date bizDate = sdf.parse( "12/12/2015" ); // milliseconds: 1449878400000 log.info( "ms: {}", bizDate.getTime() ); log.info( "date: {}", bizDate ); ... // save to db
如果该代码在UTC上的Oracle数据库上以UTC格式运行,我得到:
JVM参数:-Duser.timezone = UTC
millisecond: 1449878400000 date: Sat Dec 12 00:00:00 UTC 2015 in oracle db: 2015-Dec-12 00:00:00 // zero time
对于未设置为UTC的JVM(例如SGT),我得到:
JVM参数:无(默认时区为SGT或UTC + 8:00)
millisecond: 1449878400000 date: Sat Dec 12 08:00:00 SGT 2015 in oracle db: 2015-Dec-12 08:00:00 // plus 8 hours
请注意,它们都具有相同的毫秒数,但它们在数据库中的插入方式不同.
我的问题是:
JDBC标准是否说它在插入之前调整Date对象?请引用你的消息来源.
如果在JVM的时区未设置为UTC时,JDBC确实在将Date对象插入数据库之前调整了它,那么为什么这样设计呢?我觉得这让它更加混乱.我原以为它会按原样插入它.想象一下,如果你使用毫秒创建一个日期(例如新的日期(1449878400000L)),它将以不同的方式存储,你没有关于你的代码将运行的JVM时区的信息.或者想象你的代码将在多个设置为JVM的JVM上运行不同的时区.
当JVM的时区设置为UTC以外的任何其他时,如何阻止JDBC调整日期?我正在使用ibatis,我可能无法直接访问PreparedStatements.
我已将SimpleDateFormat的时区设置为UTC,因为我想将被解析的日期视为UTC(或根据需要将其他时区视为).如果没有这样的要求,那就不成问题了.现在看来我需要调整Date来反转JDBC在插入之前所做的事情.
问题是Java Date
对象不存储时区.该值始终为UTC,并在给定时区(通常是JVM的默认时区)中进行解析和格式化.
Oracle DATE
列也不存储时区,但应表示用户看到的日期.在99.99%的情况下,这意味着JVM默认时区中的日期.
因此,JDBC驱动程序采用UTC中的Timestamp
/ Date
值将其转换为默认时区,并将其保存到数据库中.
您正在使用该PreparedStatement.setTimestamp(int parameterIndex, Timestamp x)
方法.要控制时区,请使用该PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
方法.引用javadoc:
java.sql.Timestamp
使用给定Calendar
对象将指定参数设置为给定值.驱动程序使用该Calendar
对象构造一个SQLTIMESTAMP
值,然后驱动程序将其发送到数据库.使用Calendar
对象,驱动程序可以计算考虑自定义时区的时间戳.如果未Calendar
指定任何对象,则驱动程序将使用默认时区,即运行应用程序的虚拟机的时区.