java.util.Date和Zoneddatetime有什么区别?

时间:2019-02-27 17:43:25

标签: java datetime java.util.date zoneddatetime

使用util.date并从浏览器给日期提供服务时间,然后保存到db并取回数据库时,与直接从服务设置zoneddatetime相比,日期时间有所不同。

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:4)

tl; dr

  

java.util.Date和Zoneddatetime有什么区别?

  • Date代表UTC时刻,而ZonedDateTime代表特定时区的时刻。
  • Date是一个可怕的类,充满设计缺陷,不应该使用,而ZonedDateTime java.time 包中的现代类,您将使用觉得很有用。

Java附带了两个非常不同的框架来处理日期时间工作:一组尴尬且失败的遗留类集,以及java.time包中找到的一组现代业界领先的类。

旧版➙现代:

  • java.util.Datejava.time.Instant取代
    • 两者都代表UTC的时刻。
  • java.util.GregorianCalendarjava.time.ZonedDateTime取代
    • 两者都代表在特定时区看到的时刻。

table of legacy and modern classes for date-time handling in Java

java.util.Date

Date类代表UTC的时刻。即,日期,一天中的时间以及UTC的上下文。

内部,它是自UTC 1970年1月的第一时刻的纪元参考日期1970-01-01T00:00:00Z起的毫秒数。

使事情复杂化:

  • 创建时会捕获一个时区,该时区存储在内部,没有获取器或设置器。因此,尽管它确实适用于此类使用equals实现的问题,但在大多数情况下我们可以忽略此区域。
  • 调用toString时,此类具有非常的令人困惑的行为,即在生成表示该对象值的文本时动态应用JVM的当前时区。尽管出于良好目的,但这种反功能在试图学习日期时间处理的Java程序员中造成了无法估量的痛苦。

困惑吗?是的,这堂课令人迷惑,糟糕的设计决策混乱不堪。后来又添加了java.util.CalendarGregorianCalendar,使情况更加复杂。

与最早的Java版本捆绑在一起的所有这些麻烦的日期时间类现在都完全被 java.time 类所取代。

尤其是java.util.Datejava.time.Instant代替。从1970年UTC时代开始,两者都代表UTC的时刻。但是Instant具有更好的分辨率,nanoseconds而不是milliseconds

您可以通过调用添加到旧类的新方法来在旧类Date和现代类Instant之间来回转换。通常,您将避免使用Date。但是当与尚未更新为 java.time 的旧代码接口时,您可能需要进行转换。

java.time.ZonedDateTime

现代类ZonedDateTime代表了某个地区(某个时区)的人们使用的挂钟时间所看到的时刻。

因此InstantZonedDateTime相似之处在于它们都代表一个时刻,即该时间轴上的特定点。区别在于ZonedDateTime知道时区的规则。因此,ZonedDateTime知道如何解决诸如夏令时(DST)或政客要求的其他时间变更等异常情况。

您可以将其视为:

  

ZonedDateTime =(Instant + ZoneId

通过将时区(ZoneId)应用于Instant对象,我们可以轻松地从UTC调整到某个时区。

Instant instant = Instant.now() ;             // Capture the current moment as seen in UTC.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;     // Apply a time zone to see the same moment through the wall-clock time in use by the people of a particular region (a time zone). 

请参阅此code run live at IdeOne.com。请注意不同的日期和不同的时间,但同时具有相同的时刻。

  

instant.toString():2019-02-27T19:32:43.366Z

     

zdt.toString():2019-02-28T04:32:43.366 + 09:00 [亚洲/东京]

关键概念:InstantZonedDateTime都代表同一时刻,时间轴上的同一同时点。它们的挂钟时间不同。例如,如果日本某人给冰岛某人打电话(一直使用UTC作为时钟),并且他们俩都抬头看着各自墙上挂着的时钟,他们会看到不同的时间,并且可能甚至每月日历上的另一个日期。相同的时刻,不同的时钟时间。

对于旧类,ZonedDateTime的等效项是GregorianCalendar,这是java.util.Calendar的具体实现。确实,旧类GregorianCalendar获得了用于转换to / from ZonedDateTime的新方法。

ZonedDateTime zdt = myGregorianCalendar.toZonedDateTime();  // Convert from legacy to modern class.

…和…

GregorianCalendar gc = GregorianCalendar.from( zdt ) ;      // Convert from modern to legacy class.

结论

因此Date等同于Instant,两者都是UTC时刻。但是ZonedDateTime两者都不同,因为时区通过应用区域人民的挂钟时间调整镜头来调整了对瞬间的感知。

提示:

  • 请勿使用Date交给Date时,请立即转换为Instant。然后继续您的业务逻辑。
  • 在UTC中完成大部分工作。通常应在UTC中完成跟踪时刻,调试,日志记录,交换日期时间值以及保存到数据库的操作。学会在从事程序员工作时忘记自己的时区。将桌上的第二个时钟设置为UTC。

数据库

问题提到数据库工作。这是一个简短的摘要。搜索堆栈溢出以获取更多详细信息,因为已经处理了很多次。

从JDBC 4.2开始,我们可以直接与数据库交换 java.time 对象。使用PreparedStatement::setObjectResultSet::getObject。无需再碰到可怕的java.sql.*类,例如java.sql.Timestamp

也许可以交换Instant,但是JDBC规范不需要这样做。规范反而要求OffsetDateTime

检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

还有存储。

myPreparedStatement.setObject( … , odt ) ;

如果手头有一个Instant,请使用常量OffsetDateTime转换为ZoneOffset.UTC

OffsetDateTime odt = Instant.atOffset( ZoneOffset.UTC ) ;

要查看某个区域的挂钟时间,请应用ZoneId

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

要将ZonedDateTime存储到数据库,请转换为OffsetDateTime。这会剥夺时区信息(该地区人民根据政客的决定,对过去,现在和将来进行的更改的历史记录),留下日期,时间和偏移量-from-UTC(小时-分钟-秒的数量)。

OffsetDateTime odt = zdt.toOffsetDateTime(); 

大多数数据库在UTC中为SQL标准类型TIMESTAMP WITH TIMESTAMP的列存储时间。将OffsetDateTime提交到数据库时,您的JDBC驱动程序很可能会将OffsetDateTime中的偏移量调整为零小时-分钟-秒(以UTC本身为准)。但是我喜欢明确地这样做。它使调试更加容易,并且向读者展示了我对存储在UTC中的那一刻的理解。

OffsetDateTime odt = zdt.toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC ) ; 

答案 1 :(得分:1)

java.util.Date是几乎不推荐使用的类,它为毫秒值提供了包装。它不提供有关时区的任何信息,也没有任何有用的方法,您应始终避免使用它。

ZonedDateTime是新的JDK8日期API中的类,该API在ISO-8601日历系统中提供带有时区的日期时间。您应该检查

Oracle's explanation for new date format

答案 2 :(得分:1)

十六年前,我为 JavaWorld 杂志写了一篇名为What's Your Time Zone的文章,其中涉及您在使用类java.util.Date时描述的问题。为了“查看”类java.util.Date的实例的值,您必须将其转换为String。在Java 1.8之前,此转换总是也考虑了时区。这就是为什么在Java 1.8中引入了新的Date-Time API的原因之一。为Java版本1.8及更高版本编写的新代码应使用新的API。我相信您也应该考虑将旧代码转换为使用新的API。