JDBC 在将日期插入 Oracle DB 之前是否调整日期?我该如何防止这种情况?
Does JDBC adjust the Date before it inserts it into Oracle DB? How do I prevent this?
假设我们有以下创建日期的代码:
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 的 JVM 上运行在 UTC 的 Oracle 数据库上,我得到:
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(例如 SGT)的 JVM,我得到:
JVM 参数:none(默认时区为 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 对象插入数据库之前调整了它,为什么要这样设计?我觉得这让事情变得更加混乱。我期待它会按原样插入。想象一下,如果您使用毫秒创建一个日期(例如 new Date( 1449878400000L ) )并且它将以不同的方式存储并且您没有关于 JVM 时区的信息,您的代码将在 运行 中。或者想象您的代码将是 运行 在设置为不同时区的多个 JVM 上。
当 JVM 的时区设置为 UTC 以外的任何时区时,如何防止 JDBC 调整日期?我正在使用 ibatis,我可能无法直接访问 PreparedStatements。
我已将 SimpleDateFormat 的时区设置为 UTC,因为我想将解析的日期视为 UTC(或根据需要作为其他时区)。如果没有这样的要求,那将不是问题。现在看来我需要调整日期以反转 JDBC 在插入它之前正在做的事情。
问题是 Java Date
对象不存储时区。该值在 UTC 中是 always,并在给定的时区进行解析和格式化,通常是 JVM 的默认时区。
Oracle DATE
列也没有时区存储,但应该表示用户看到的日期。在 99.99% 的情况下,这意味着 JVM 默认时区中的日期。
因此,JDBC 驱动程序采用 UTC 中的 Timestamp
/ Date
值,将其转换为默认时区,并将其保存到数据库中。
您正在使用 PreparedStatement.setTimestamp(int parameterIndex, Timestamp x)
method. To control the time zone, use the PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
方法。引用 javadoc:
Sets the designated parameter to the given java.sql.Timestamp
value, using the given Calendar
object. The driver uses the Calendar
object to construct an SQL TIMESTAMP
value, which the driver then sends to the database. With a Calendar
object, the driver can calculate the timestamp taking into account a custom timezone. If no Calendar
object is specified, the driver uses the default timezone, which is that of the virtual machine running the application.
假设我们有以下创建日期的代码:
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 的 JVM 上运行在 UTC 的 Oracle 数据库上,我得到:
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(例如 SGT)的 JVM,我得到:
JVM 参数:none(默认时区为 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 对象插入数据库之前调整了它,为什么要这样设计?我觉得这让事情变得更加混乱。我期待它会按原样插入。想象一下,如果您使用毫秒创建一个日期(例如 new Date( 1449878400000L ) )并且它将以不同的方式存储并且您没有关于 JVM 时区的信息,您的代码将在 运行 中。或者想象您的代码将是 运行 在设置为不同时区的多个 JVM 上。
当 JVM 的时区设置为 UTC 以外的任何时区时,如何防止 JDBC 调整日期?我正在使用 ibatis,我可能无法直接访问 PreparedStatements。
我已将 SimpleDateFormat 的时区设置为 UTC,因为我想将解析的日期视为 UTC(或根据需要作为其他时区)。如果没有这样的要求,那将不是问题。现在看来我需要调整日期以反转 JDBC 在插入它之前正在做的事情。
问题是 Java Date
对象不存储时区。该值在 UTC 中是 always,并在给定的时区进行解析和格式化,通常是 JVM 的默认时区。
Oracle DATE
列也没有时区存储,但应该表示用户看到的日期。在 99.99% 的情况下,这意味着 JVM 默认时区中的日期。
因此,JDBC 驱动程序采用 UTC 中的 Timestamp
/ Date
值,将其转换为默认时区,并将其保存到数据库中。
您正在使用 PreparedStatement.setTimestamp(int parameterIndex, Timestamp x)
method. To control the time zone, use the PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
方法。引用 javadoc:
Sets the designated parameter to the given
java.sql.Timestamp
value, using the givenCalendar
object. The driver uses theCalendar
object to construct an SQLTIMESTAMP
value, which the driver then sends to the database. With aCalendar
object, the driver can calculate the timestamp taking into account a custom timezone. If noCalendar
object is specified, the driver uses the default timezone, which is that of the virtual machine running the application.