sup*_*sky 7 java timezone date jdbc ojdbc
这个问题与此有关,但这个具体问题侧重于原因。所以不,这不是重复的。
引用答案:
问题是 Java Date 对象不存储时区。该值始终采用 UTC,并在给定的时区(通常是 JVM 的默认时区)中解析和格式化。
Oracle DATE 列也没有时区存储,但应表示用户看到的日期。在 99.99% 的情况下,这意味着 JVM 的默认时区中的日期。
因此,JDBC 驱动程序采用 UTC 格式的时间戳/日期值,将其转换为默认时区,并将其保存到数据库中。
这些问题的答案就是为什么。
我看不到设计的好处,我只能看到与之相关的问题。一个恰当的例子是在特定时区完成保存而在另一个时区完成检索。在这个特定主题上抛出的问题数量正好证明了我的观点。
所以最终的问题是,为什么它是这样设计的?原因是什么?
Bas*_*que 10
日期时间处理是一个非常复杂的话题。作为程序员,我们对时间的直观理解对我们不利,使这个主题难以掌握。此外,旧数据库和旧类中糟糕的日期时间处理使工作更加混乱。
首先,避免与最早版本的 Java 捆绑在一起的糟糕的旧日期时间类。切勿使用java.util.Date、java.util.Calendar、java.sql.Timestamp或其他相关类。仅使用java.time类。如果您必须与尚未更新为java.time 的旧代码交互,请调用添加到旧类的新转换方法。
Date被替换了Instant。
问题是 Java Date 对象不存储时区。
不对。An Instant(和 a Date)始终采用 UTC。现代类和遗留类都表示自 UTC 时间 1970 年第一时刻 1970-01-01T00:00:00Z 以来的小数秒计数。总是在 UTC,easy-peasy。
Oracle DATE 列的存储也没有时区,
真的。
所述的OracleDATE数据类型表示仅与时区中的日期,但缺乏时区的任何概念或偏移从-UTC。这显然是一种遗留类型,在 SQL 标准定义一些基本的日期时间类型之前创建。在标准中,TIMESTAMP WITHOUT TIME ZONE可能映射接近 Oracle DATE。
但应该代表用户看到的日期。在 99.99% 的情况下,这意味着 JVM 的默认时区中的日期。
我不明白作者这句话是什么意思。我认为这是他们笨拙的说法,任何类似于 SQL 标准的类型TIMESTAMP WITHOUT TIME ZONE只是按原样采用任何给定日期或日期与时间,而不尝试在区域或偏移量之间进行调整。因此,如果您在 2018 年 1 月 21 日中午通过,它会存储一个与此字符串等效的值,2018-01-23T12:00而不管是魁北克蒙特利尔的中午还是印度加尔各答的中午(两个不同的时刻,相隔几个小时)。
因此,JDBC 驱动程序采用 UTC 格式的时间戳/日期值,将其转换为默认时区,并将其保存到数据库中。
虽然此处未指定 JDBC 驱动程序,但我怀疑这是它的行为。这种行为将与DATE没有这种区域调整的 Oracle类型的行为相矛盾。OracleDATE类型(在我阅读文档时;我不是 Oracle 用户)是不可知的或不知道区域/偏移量。
在 Java 中,映射到 SQL 标准TIMESTAMP WITHOUT TIME ZONE和 Oracle 的类DATE是LocalDateTime. 您应该仅在以下三种情况下使用这些无区域类型:
LocalDateTime. 要打印报告或显示日历,请动态应用时区 ( ZoneId) 以生成特定时刻 (ZonedDateTime或Instant)。这必须即时完成,而不是存储值。不按原样调整和保存值 (UTC) 到底有什么问题?
JDBC驱动程序应该不是做给UTC任何调整的这类OracleDate或SQL标准TIMESTAMP WITHOUT TIME ZONE。
如果 2018-06-06T21:53Z 的两个用户,一个在魁北克,一个在印度,同时将他们自己狭隘的挂钟时间的当前时刻保存到 SQL-standardTIMESTAMP WITHOUT TIME ZONE或 Oracle类型的列中DATE,那么我们应该看到两个具有值的行:
这些值不同是因为America/Montreal比 UTC 晚四个小时,而Asia/Kolkata比 UTC 早五个半小时,并且没有对时区进行调整。要再次重复自己,存储的值这里仅代表日期和时间的一天,但没有时区或偏移从-UTC,他们的任何上下文不是代表了一会儿。
混淆可能来自这样一个事实,即某些数据库(例如 Postgres)确实将传入值调整为 UTC,以便将值指向不同类型的列,即TIMESTAMP WITH TIME ZONE类型(注意WITHvs WITHOUT)。Postgres 和其他数据库使用任何传递的区域/偏移量信息来调整为 UTC 值,然后丢弃区域/偏移量信息。所以类型名称有点用词不当,您可以将其视为TIMESTAMP WITH RESPECT FOR TIME ZONE.
如果上面在 2018-06-06T21:53Z 看到的相同的两个用户将当前时刻保存到类型为 SQL 的标准列中TIMESTAMP WITH TIME ZONE,那么这两行将显示为:
所述Z在端部,发音Zulu和装置UTC。
它试图通过在将值保存到数据库之前调整值来解决什么问题?
通常,日期时间处理的最佳实践是在 UTC 而不是其他区域/偏移量中工作。
作为程序员或系统管理员工作时,忘记你自己狭隘的时区。从您自己的区域到 UTC 或其他区域来回转换会让您筋疲力尽。将 UTC 视为唯一的真实时间;所有其他区域都只是变化。
该Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多小数的9个位数)。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Run Code Online (Sandbox Code Playgroud)
店铺。
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Run Code Online (Sandbox Code Playgroud)
取回。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Run Code Online (Sandbox Code Playgroud)
在特定时区呈现那个时刻。
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
Run Code Online (Sandbox Code Playgroud)
该java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310。
您可以直接与您的数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*
从哪里获得 java.time 类?
该ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如Interval,YearWeek,YearQuarter,和更多。