Before writing a Java Date to an SQL TIMESTAMP column, does JDBC translate the date from the JVM time zone to the database session time zone?
在将Java Date写入SQL TIMESTAMP列之前,JDBC是否将日期从Java虚拟机时区转换为数据库会话的日期?
例如,假设Java虚拟机时区为UTC且数据库会话时区为UTC-5。 如果Java程序试图通过将2000-01-01 00:00:00传递给PreparedStatement#setTimestamp(int, Timestamp)来存储2000-01-01 00:00:00,那么根据JDBC标准,数据库是否会存储TIMESTAMP '2000-01-01 00:00:00'或TIMESTAMP '1999-12-31 19:00:00'?
-
时区(或应该)仅与日期的呈现相关,而不是与存储相关。 所以不应该进行翻译。 然而,这是猜测,而不是答案。
-
那么RedGrittyBrick,您希望数据库存储TIMESTAMP '2000-01-01 00:00:00'吗?
-
RedGrittyBrick,如果您存储时区,时区与日期相关。 但是,SQL TIMESTAMP列不存储时区,因此存储独立于位置的日期(即相对于UTC时区)是有意义的。
-
RedGrittyBrick,我试图理解的主要问题是,如果有的话,JDBC标准如何要求JDBC驱动程序实现将时区应用于在JDBC客户端和数据库服务器之间传输日期的日期。
-
另请参阅:stackoverflow.com/questions/2858182/…
不,JDBC只是客户端如何访问数据库的API。对于时间戳存储,这必须由编写符合JDBC API标准的数据库驱动程序的组织所依赖。
这是MySQL的PreparedStatement实现的实现。他们似乎把Java的JVM时区带到MySQL Timezone(检查setTimestampInternal()方法)。
-
是的,你是对的。从MySQL JDBC驱动程序实现中可以清楚地看出,setTimestampInternal()将给定日期转换为服务器时区(x = TimeUtil.changeTimezone(this.connection, x, tz, 2059 this.connection.getServerTimezone());)。它还使用依赖于语言环境的SimpleDateFormat将日期格式化为字符串。
-
相比之下,setTimestamp()的PostgresQL JDBC驱动程序实现使用JVM时区转换给定日期。请参阅cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/&hellip上的方法AbstractJdbc2Statement#setTimestamp(int, Timestamp, Calendar);和TimestampUtils#toString(Calendar, Timestamp) at cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/…。时区处理依赖于JDBC驱动程序!
-
@Derek Mahar,这就是我所说的。 API的实现因编写JDBC驱动程序的组织而异。如果MySQL(示例)在存储之前转换时区,那么您不应该认为它是绝对的,SQLite(示例)会(它们根本不会)。
-
Elite,我用我的PostgreSQL JDBC驱动程序示例确认了你所说的内容。我试图表达我同意你的意见,但在评论栏中用完了字符,所以我的最后一句话可能会出现分歧。但是,我真正想说的是,"所以,你是对的,时区处理依赖于JDBC驱动程序!"
-
好的....抱歉误解了你的陈述。不要忘记接受帮助你的答案。 :)
-
@Buhake Sindi你能评论一下ORACLE PreparedStatement是如何工作的吗?
-
@Kanagavelu Sugumar,为什么?你想知道什么?
-
@Buhake Sindi请回答我stackoverflow.com/questions/14070572/…
-
SQL Server将datetime字段作为自1900年以来的日期和白天的毫秒数并将其设置为本地Calendar(1900, 0, dayN),如果您要使用UTC - 在驱动程序加载之前需要TimeZone.setDefault(TimeZone.getTimeZone("GMT+00:00"));,请参阅stackoverflow.com/a/29705750 / 173149
现在我的要求是它应该以GMT / UTC存储值,而不管JVM的时区。有没有办法在运行时设置时区,然后在完成JDBC后取消设置时区?
编辑:
好的,我找到了解决这个问题的方法。做了以下
-
如果其他线程在日期同时进行计算,假设默认时区不是UTC,则可能会导致竞争条件。
-
注意更改默认时区是有风险的业务。此更改会影响此JVM中运行的所有线程中的所有代码,而不仅仅是方法中的本地代码。
您可以使用重载的setTimestamp setter接受Calendar实例来指定时区
示例(如果您使用的是Joda日期时间):
1 2
| org.joda.time.DateTime sendDateUTC = new DateTime( DateTimeZone.UTC ).withMillis( millis );
statement.setTimestamp (1, sendDateUTC, sendDateUTC.toGregorianCalendar() ); |
根据javaDoc:
使用给定的Calendar对象将指定参数设置为给定的java.sql.Timestamp值。驱动程序使用Calendar对象构造SQL TIMESTAMP值,然后驱动程序将其发送到数据库。使用Calendar对象,驱动程序可以计算考虑自定义时区的时间戳。如果未指定Calendar对象,则驱动程序将使用默认时区,即运行应用程序的虚拟机的时区。
规范很傻。 java.util.Date存储GMT参考帧中纪元的毫秒数。 Java.sql.Timestamp是同一参考帧中的Date加纳秒。所有未弃用的getter和setter都使用GMT参考框架。对于任何类型的健全性,存储时间戳的默认时区应为GMT。
在多层应用程序中,前端,驱动程序和数据库服务器都可以位于不同的时区。有些层可能同时位于不同的时区;例如,如果您在整个大陆进行互联网负载平衡,或者您有一个移动应用程序连接到中央服务器。云操作环境与您不知道JDBC驱动程序将在何处运行的情况大致相同,也不会保证永远不会发生变化。
我知道在这些环境中实现一致性的唯一方法是仅使用接受日历的参数setter和ResultSet getter,并确保访问数据的每个应用程序都使用某些日历,最好是GMT或UTC。
-
克里斯,我不确定你是否回答了德里克的问题。 jdbc会根据客户端和服务器之间的时区差异调整时间戳吗?我很确定答案是否定的。
-
是的,答案仍然是'不';至少它应该是。我只是在解释java.sql.Date和java.Util.Date之间的区别,因为原始的海报没有区分他们正在谈论的内容。此外,无法保证所有JDBC驱动程序的行为方式相同,因为有些实际上将时区信息嵌入其内部类型,而有些则附加到没有此功能的后端。指定日历可减少不确定性。否则,如果您的后端不保留时区,并且您的客户存在于多个时区,那么这些值意味着什么?