龙空技术网

JDK8 新特性之新的日期&时间API,一篇讲清楚!

千锋教育 486

前言:

现在你们对“java标准时间”大体比较着重,兄弟们都需要知道一些“java标准时间”的相关资讯。那么小编在网摘上汇集了一些有关“java标准时间””的相关知识,希望大家能喜欢,你们快快来了解一下吧!

本系列内容为:从零开始学Java,为千锋教育资深Java教学老师独家创作!

致力于为大家讲解清晰Java相关知识点,含有丰富的代码案例及讲解。如果感觉对大家有帮助的话,可以【关注】持续追更~

技术类问题,也欢迎大家和我们沟通交流!

前言

在上一篇文章中本系列内容给大家讲解了Java里的格式化问题,这样我们就可以个性化设置日期时间的展示方式了。似乎我们现在已经掌握了不少关于日期和时间的操作技巧,但其实随着时间的不断推移,现实的需求也在不断更新,原先的一些API已经难以满足开发需求了。所以从JDK 8之后,为了满足更多的开发需求,Java给我们增加了不少关于日期时间的新特性,接下来本篇文章就带各位来看看这些新特性有哪些。

全文大约 【5400】字, 不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考...

一. 新特性概述

在JDK 8之前,其实有不少的API都存在着一些问题,日期时间等相关类同样如此。所以从JDK 8开始,Java做了较大的改动,出现了很多新特性。其中,java.time包中了就提供了不少新的日期和时间API,主要如下:

● 本地日期和时间类:LocalDateTime,LocalDate,LocalTime;

● 带时区的日期和时间类:ZonedDateTime;

● 时刻类:Instant;

● 时区:ZoneId,ZoneOffset;

● 时间间隔:Duration。

在格式化操作方面,也推出了一个新的格式化类DateTimeFormatter。

和之前那些旧的API相比,新的时间API严格区分了时刻、本地日期、本地时间和带时区的日期时间,日期和时间的运算更加方便。这些新的API类型几乎全都是final不变类型,我们不必担心其会被修改。并且新的API还修正了旧API中一些不合理的常量设计:

● Month的范围采用1~12,分别表示1月到12月;

● Week的范围采用1~7,分别表示周一到周日。

二. LocalDateTime

1. 简介

LocalDateTime是JDK 8之后出现的,用来表示本地日期和时间的类。我们可以通过now()方法,默认获取到本地时区的日期和时间。与之前的旧API不同,LocalDateTime、LocalDate和LocalTime默认会严格按照ISO 8601规定的日期和时间格式进行打印。

2. 获取当前日期和时间

我们可以通过now()方法来获取到当前的日期和时间。我们在执行一行代码时,多少也会消耗一点时间,日期和时间的值可能会对不上,尤其是时间的毫秒数可能会有差异。所以为了保证获取到的日期和时间值差异减少,我们的代码尽量要编写如下:

3. of()方法的作用

我们可以通过of()方法,根据指定的日期和时间来创建一个LocalDateTime对象,用法如下:

4. parse()方法的作用

我们可以使用parse()方法,将一个时间格式的字符串解析为LocalDateTime,用法如下:

我们要注意,根据ISO 8601规定,日期和时间的分隔符是T,标准格式如下:

● 日期:yyyy-MM-dd

● 时间:HH:mm:ss

● 带毫秒的时间:HH:mm:ss.SSS

● 日期和时间:yyyy-MM-dd'T'HH:mm:ss

● 带毫秒的日期和时间:yyyy-MM-dd'T'HH:mm:ss.SSS

5. 时间加减方法

在LocalDateTime中,给我们提供了一系列的加减操作方法,使得我们可以很轻易的实现时间的加减操作,比如给某个日期或时间进行加或减的操作。plusXxx()增加方法如下图所示:

minusXxx()减少方法如下图所示:

6. 时间调整方法

我们除了可以进行日期和时间的增加、减少操作之外,还可以利用withXxx()方法对日期和时间进行调整,这些方法如下图所示:

从上图可以看出,我们可以对年、月、日、时、分、秒等进行调整,具体含义如下:

● 调整年:withYear()

● 调整月:withMonth()

● 调整日:withDayOfMonth()

● 调整时:withHour()

● 调整分:withMinute()

● 调整秒:withSecond()

我们在利用withXxx()方法调整时间是,在调整月份时,会相应地调整日期。并且要注意,如果某个月中没有29、30、31等日期,会出现java.time.DateTimeException: Invalid date 'FEBRUARY 31'类似的异常,如下图:

7. with()方法

LocalDateTime中有一个通用的with()方法,允许我们进行更复杂的运算,比如获取当月或下个月的第一天、最后一天、第一个周一等操作。

8. isBefore()与isAfter()方法

如果我们判断两个LocalDateTime的先后顺序,可以使用isBefore()、isAfter()方法。

9. Duration时间间隔和Period间隔天数

我们可以使用Duration表示两个时刻之间的时间间隔,用Period表示两个日期之间的间隔天数。

Duration和Period的表示方法也符合ISO 8601的格式,它以P...T...的形式表示。P...T之间表示日期间隔,T后面表示时间间隔,如果是PT...的格式表示仅有时间间隔。

所以如果是两个LocalDateTime之间的差值,要使用Duration表示,形式为PT12H10M30S,意思是间隔12小时10分钟30秒。而两个LocalDate之间的差值用Period表示,形式为P1M21D,表示间隔1个月21天。

三. ZonedDateTime

1. 简介

我们知道,LocalDateTime表示本地日期和时间,如果我们要表示一个带时区的日期和时间,就需要使用ZonedDateTime了。ZonedDateTime相当于是LocalDateTime + ZoneId,其中ZoneId是java.time引入的新的时区类,它与旧的java.util.TimeZone是有区别的。在ZonedDateTime中也提供了plusDays()等加减操作,使用起来也非常地方便。

2. 创建方式

如果我们想要创建一个ZonedDateTime对象,可以有以下几种方法:

● 通过now()方法返回ZonedDateTime对象;

● 通过给LocalDateTime附加ZoneId获取。

接下来我们就通过一些具体的案例来给大家展示一下ZonedDateTime到底是怎么创建的

2.1 now()方法

我们先来看看通过now()方法如何创建一个ZonedDateTime对象。

在这个案例中,我们获得的两个ZonedDateTime对象,它们的时区虽然不同,但时间都是同一时刻的,如果毫秒数不同是在执行语句时有一点时间差。

2.2 附加ZoneId

我们再来通过给LocalDateTime附加ZoneId的方式来获取一个LocalDateTime对象。

这种方式创建的ZonedDateTime对象,其日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻。

3. 时区转换

有时候我们的项目中,需要将A时区的时间转换成B时区的时间,要在两个时区直接进行切换。以前的时候就需要我们自己计算,非得的麻烦且复杂,现在新的Java API中直接给我们带了负责时区转换的方法。比如withZoneSameInstant()方法,就可以将一个关联的时区转换到另一个时区,转换后的日期和时间也会相应调整。

注意:

时区的名字不能随便瞎写,否则会产生

java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/Beijing,

如下图所示:

而且在时区转换时,由于夏令时的存在,不同的日期转换结果也有可能是不同的,有可能会出现两次转换后有1小时的夏令时时差。我们以后涉及到时区时,尽量不要自己计算时差,否则难以正确处理夏令时。

四. DateTimeFormatter

1. 简介

我们在前面学习Date日期时间对象时,知道该对象默认输出的时间格式其实是不符合大多数的使用场景的,所以就需要我们对其进行格式化设置,比如通过printf()方法或SimpleDateFormat类来实现。但是当我们使用新的LocalDateTime或ZonedDateTime需要进行格式化显示时,就要使用新的DateTimeFormatter类了。

和SimpleDateFormat不同的是,DateTimeFormatter不但是不可变的对象,且还是线程安全的。在代码中,我们可以创建出一个DateTimeFormatter实例对象,到处引用。而之前的SimpleDateFormat是线程不安全的,使用时只能在方法内部创建出一个新的局部变量。

2. 创建方式

我们要想使用DateTimeFormatter,首先得创建出一个DateTimeFormatter对象,一般有如下两种方式:

● DateTimeFormatter.ofPattern(String pattern):pattern是待传入的格式化字符串;

● DateTimeFormatter.ofPattern(String pattern,Locale locale):locale是所采用的本地化设置。

3. 基本使用

了解了创建方式之后,我们就可以来看看该如何进行时间格式化了。

当我们在格式化字符串时,如果需要输出一些固定的字符,可以用'xxx'的形式来表示。

另外我们在调用System.out.println()方法对一个ZonedDateTime,或者LocalDateTime实例进行打印时,实际上调用的是它们的toString()方法。默认的toString()方法,显示的字符串是按照ISO 8601格式显示的,我们可以通过DateTimeFormatter预定义的几个静态变量来引用。

五. Instant

1. 简介

在之前给大家讲过,计算机中存储的当前时间,其实就是一个不断递增的整数,即从1970年1月1日0分0秒开始以来不断递增的一个整数值。要想获取这个整数值,我们可以使用System.currentTimeMillis()方法,该方法会返回一个long型的毫秒值,这就是当前时间的时间戳!

现在,我们其实还可以使用Instant.now()来获取当前的时间戳,效果和System.currentTimeMillis()类似。但Instant获取的时间戳更为精确,内部采用了两种时间精度,分别是秒和纳秒。

另外Instant还提供了plusXxx和minusXxx增减方法,方便我们进行时间的操作。且Instant作为时间戳对象,我们还可以给它附加上一个时区,创建出对应的ZonedDateTime对象。也可以给它关联上指定的ZoneId,得到对应的ZonedDateTime,进而获得对应时区的LocalDateTime。所以我们可以在LocalDateTime、ZoneId、Instant、ZonedDateTime之间互相转换。

2. 使用方法

接下来我们看看Instant的用法。

六. 新旧时间API的转换

1、简介

现在我们知道,在Java中,关于日期和时间的API其实有两套,一套旧的,一套新的。这两套API以JDK 8为分割线,此前的属于旧版API,此后的属于新版API

● 旧版API:定义在java.util包中,主要包括Date、Calendar和TimeZone几个类;

● 新版API:JDK 8之后引入,定义在java.time包中,主要包括LocalDateTime、ZonedDateTime、ZoneId、DateTimeFormatter、Instant等。

这时有些同学就会好奇,这两套时间API我们在开发时该怎么选择?

1、如果大家的项目属于是一个新开发的项目,且你们的Java版本在JDK 8以上,那就采用新版的API吧。

2、但是如果你们的项目涉及到遗留代码,是对旧的项目进行维护或改造,很多遗留代码仍然在使用旧的API,建议大家还是不要改变原有的选择,请继续使用旧版API。

3、但是如果你们的项目环境已经从低版本的JDK切换到了高本版的JDK,且你们公司要求对项目进行升级改造,我们能不能在新旧两种API之间进行转换呢? 其实也是可以的,今天就给大家讲一下如何在新旧两套API之间互相转换。

2. 旧转新

首先我们来看看旧的API是如何转成新的API的。比如我们想把旧式的Date或Calendar转换为新的API对象,可以通过toInstant()方法转换为Instant对象,然后再继续转换为ZonedDateTime,实现代码如下:

3. 新转旧

旧版API可以转换成新版API,同时我们也可以将新班API转成旧版的API,已实现与原有系统的兼容。如果要实现这一目标,我们需要借助long型的时间戳做一个“中转”,具体实现如下:

七. 结语

至此,就把日期的格式化操作给大家讲解完毕了,今天的内容比较多且很实用,最后我们总结一下今日重点

● JDK 8中引入了新的日期和时间API,它们是不变类,默认按ISO 8601标准格式化和解析;

● 使用LocalDateTime可以非常方便地对日期和时间进行加减、调整日期和时间,且总是返回新对象;

● 使用isBefore()和isAfter()可以判断日期和时间的先后;

● 使用Duration和Period可以表示两个日期和时间的“区间间隔”;

● ZonedDateTime是带时区的日期和时间,可用于时区转换;

● ZonedDateTime和LocalDateTime可以相互转换;

● 对ZonedDateTime或LocalDateTime进行格式化,需要使用DateTimeFormatter类;

● DateTimeFormatter可以通过格式化字符串和Locale对日期和时间进行定制输出;

● Instant表示高精度时间戳,它可以和ZonedDateTime以及long互相转换;

● 处理日期和时间时,尽量使用新的java.time包;

● 新旧两种API之间可以进行转换,旧版转新版可以通过toInstant()方法转换为Instant对象,然后再继续转换为ZonedDateTime;新版转旧版需要借助long型的时间戳做一个“中转”。

以上就是我们本篇内容的详细讲解了,大家学会了吗

有技术类问题欢迎大家和我们一起交流讨论~

更多技术类干货内容/程序员信息/IT相关,关注@千锋教育

标签: #java标准时间