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

Posted qian-fen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK8 新特性之新的日期&时间API,一篇讲清楚!相关的知识,希望对你有一定的参考价值。

本系列为:从零开始学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()方法来获取到当前的日期和时间。我们在执行一行代码时,多少也会消耗一点时间,日期和时间的值可能会对不上,尤其是时间的毫秒数可能会有差异。所以为了保证获取到的日期和时间值差异减少,我们的代码尽量要编写如下:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Demo10 

	public static void main(String[] args) 
		//获取当前日期和时间
		LocalDateTime dt = LocalDateTime.now();
		System.out.println("dt="+dt);
		
		// 转换到当前日期
		LocalDate d = dt.toLocalDate(); 
		System.out.println("date="+d);
		
		// 转换到当前时间
		LocalTime t = dt.toLocalTime(); 
		System.out.println("time="+t);
	


3. of()方法的作用

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Demo11 

	public static void main(String[] args) 
		// 根据指定的日期和时间创建LocalDateTime对象

		// 2023-01-25, 月份与之前不同:1-12分别表示1-12个月
		LocalDate date = LocalDate.of(2023, 1, 25);
		// 20:35:48
		LocalTime time = LocalTime.of(20, 35, 48);
		LocalDateTime dt1 = LocalDateTime.of(date, time);
		System.out.println("dt1=" + dt1);

		LocalDateTime dt2 = LocalDateTime.of(2023, 1, 23, 20, 35, 48);
		System.out.println("dt2=" + dt2);
	

4. parse()方法的作用

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Demo11 

	public static void main(String[] args) 
		// 解析时间字符串。T是日期和时间的分隔符
		LocalDateTime dt = LocalDateTime.parse("2023-02-22T20:18:15");
		System.out.println("dt="+dt);
		
		LocalDate date = LocalDate.parse("2012-10-15");
		System.out.println("date="+date);
		
		LocalTime time = LocalTime.parse("16:15:20");
		System.out.println("time="+time);
	

我们要注意,根据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()减少方法如下图所示:

import java.time.LocalDateTime;

public class Demo13 

	public static void main(String[] args) 
		// 对日期进行加减操作
		LocalDateTime dt1 = LocalDateTime.now();
		System.out.println("dt1=" + dt1);
		
        // 加3天减6小时:
        LocalDateTime dt2 = dt1.plusDays(3).minusHours(6);
        // 2019-10-31T17:30:59
        System.out.println("dt2=" +dt2); 
        
        // 减1月:
        LocalDateTime dt3 = dt2.minusMonths(1);
        // 2019-09-30T17:30:59
        System.out.println("dt3=" +dt3); 
        
        //加两周
        LocalDateTime dt4 = dt3.plusWeeks(2);
        System.out.println("dt4=" +dt4); 
	


6. 时间调整方法

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

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

● 调整年:withYear()

● 调整月:withMonth()

● 调整日:withDayOfMonth()

● 调整时:withHour()

● 调整分:withMinute()

● 调整秒:withSecond()

import java.time.LocalDateTime;

public class Demo14 

	public static void main(String[] args) 
		// 对日期进行加减操作
		LocalDateTime dt1 = LocalDateTime.now();
		System.out.println("dt1=" + dt1);
		
		// 注意:如果某个月中没有29、30、31等日期,会出现java.time.DateTimeException: Invalid date \'FEBRUARY 31\'类似的异常。
        //LocalDateTime dt2 = dt1.withDayOfMonth(31);
		
		//日期变为25日
        LocalDateTime dt2 = dt1.withDayOfMonth(25);
        System.out.println("dt2="+dt2); // 2019-10-31T20:30:59
        
        //月份变成5月
        LocalDateTime dt3 = dt2.withMonth(5);
        //2023-05-25T20:06:06.768
        System.out.println("dt3="+dt3); 
		
        //年份变成2024年
        LocalDateTime dt4 = dt3.withYear(2024);
        //2024-05-25T20:06:06.768
        System.out.println("dt4=" +dt4); 
	


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

7. with()方法

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

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class Demo14 

	public static void main(String[] args) 
        //获取本月的第一天0:00时刻:
        LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
        System.out.println("firstDay="+firstDay);

        //获取本月的最后1天:
        LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("lastDay="+lastDay);

        //获取下个月的第1天:
        LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("nextMonthFirstDay="+nextMonthFirstDay);

        //获取本月的第1个周一
        LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
        System.out.println("firstWeekday="+firstWeekday);
	


8. isBefore()与isAfter()方法

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Demo16 

	public static void main(String[] args) 
		LocalDateTime now = LocalDateTime.now();
        LocalDateTime target = LocalDateTime.of(2022, 12, 25, 10, 15, 10);
        //判断A日期在B日期之前
        System.out.println("before?="+now.isBefore(target));
        
        //判断A日期在B日期之前
        System.out.println(LocalDate.now().isBefore(LocalDate.of(2023, 12, 19)));
        
        //判断A时间在B时间之后
        System.out.println(LocalTime.now().isAfter(LocalTime.parse("10:15:20")));
	


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

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

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

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

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;

public class Demo17 

	public static void main(String[] args) 
		LocalDateTime start = LocalDateTime.of(2023, 05, 24, 13, 15, 20);
        LocalDateTime end = LocalDateTime.of(2025, 11, 8, 19, 25, 30);
        //计算两个时间的间隔
        Duration d = Duration.between(start, end);
        //PT21582H10M10S,间隔21582小时10分10秒
        System.out.println("duration="+d); 

        //计算两个日期的间隔
        Period p = LocalDate.of(2022, 12, 11).until(LocalDate.of(2025, 2, 22));
        //P2Y2M11D,间隔2个月11天
        System.out.println("period="+p); 
        
        //我们也可以使用ofXxx()或parse()方法直接创建Duration
        //10hours
        Duration d1 = Duration.ofHours(10);
        //2day,4hours,5minutes
        Duration d2 = Duration.parse("P2DT4H5M"); 
        System.out.println("d1="+d1); 
        System.out.println("d2="+d2); 
	


三. 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对象。

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Demo18 

	public static void main(String[] args) 
		//获取默认时区的时间对象
		ZonedDateTime zdt1 = ZonedDateTime.now(); 
		System.out.println("zdt1="+zdt1); 
		
		 //获取指定时区的时间对象
        ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("zdt2="+zdt2); 
	


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

2.2 附加ZoneId

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

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Demo19 

	public static void main(String[] args) 
		LocalDateTime ldt = LocalDateTime.of(2023, 1, 25, 10, 15, 11);
		//获取默认时区的时间对象
		ZonedDateTime zdt1 = ldt.atZone(ZoneId.systemDefault());
		System.out.println("zdt1=" + zdt1);

		//获取指定时区的时间对象
		ZonedDateTime zdt2 = ldt.atZone(ZoneId.of("America/New_York"));
		System.out.println("zdt2=" + zdt2);
	


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

3. 时区转换

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

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Demo20 
	public static void main(String[] args) 
		//将北京时间转为纽约时间
		//获取北京时区的当前时间
		//注意:这里的时区名字不能随便瞎写,否则会产生java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/Beijing
        ZonedDateTime zdt1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println("zdt1=" + zdt1);
        
        //将当前时区的时间,转换为纽约时间
        ZonedDateTime zdt2 = zdt1.withZoneSameInstant(ZoneId.of("America/New_York"));
		System.out.println("zdt2=" + zdt2);

        //转换为本地时间
		LocalDateTime ldt = zdt2.toLocalDateTime();
		System.out.println("ldt=" + ldt);
	

注意:

时区的名字不能随便瞎写,否则会产生java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/Beijing,如下图所示:

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

四. DateTimeFormatter

1. 简介

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

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

2. 创建方式

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

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

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

3. 基本使用

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

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Demo21 

	public static void main(String[] args) 
		//获取默认的本地时间
		ZonedDateTime zdt = ZonedDateTime.now();
		//获取一个DateTimeFormatter对象,如果需要输出固定字符,可以用\'xxx\'表示,如\'中国时间\'
        var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd\'T\'HH:mm:ss ZZZZ \'中国时间\'");
        System.out.println(formatter.format(zdt));

        //获取一个DateTimeFormatter对象,中国时区
        var zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm:ss", Locale.CHINA);
        System.out.println(zhFormatter.format(zdt));
        //改变默认的显示格式,用指定的格式显示
        //System.out.println(DateTimeFormatter.ISO_DATE.format(zdt));

        //获取一个DateTimeFormatter对象,美国时区
        var usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm:ss", Locale.US);
        //System.out.println(usFormatter.format(zdt));
        //改变默认的显示格式
        System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(zdt));
	


当我们在格式化字符串时,如果需要输出一些固定的字符,可以用\'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还提供了plusXxxminusXxx增减方法,方便我们进行时间的操作。且Instant作为时间戳对象,我们还可以给它附加上一个时区,创建出对应的ZonedDateTime对象。也可以给它关联上指定的ZoneId,得到对应的ZonedDateTime,进而获得对应时区的LocalDateTime。所以我们可以在LocalDateTime、ZoneId、Instant、ZonedDateTime之间互相转换。

2. 使用方法

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

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Demo22 

	public static void main(String[] args) 
		//获取当前时间的时间戳
		long currentTimeMillis = System.currentTimeMillis();
		System.out.println("currentTimeMillis毫秒级时间戳="+currentTimeMillis);

		//获取当前时间的时间戳
		Instant now = Instant.now();
		System.out.println("now时刻="+now);
		// 秒
        System.out.println("秒级时间戳="+now.getEpochSecond()); 
        // 毫秒
        System.out.println("毫秒级时间戳="+now.toEpochMilli());
		
		//用指定的时间戳创建Instant对象
		Instant ins = Instant.ofEpochSecond(1676262418);
		//获取所在时区的ZonedDateTime对象
		ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
		System.out.println("zdt="+zdt); 
	


六. 新旧时间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,实现代码如下:

import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;

public class Demo24 

	public static void main(String[] args) 
		//将旧版API中的Date,转为新版API中的Instant对象
		Instant instant = new Date().toInstant();
		System.out.println("instant="+instant);
		
		//将旧版API中的Calendar,转为新版API中的Instant,然后再进一步转为新版的ZonedDateTime:
		Calendar calendar = Calendar.getInstance();
		Instant instant2 = calendar.toInstant();
		ZonedDateTime zdt = instant2.atZone(calendar.getTimeZone().toZoneId());
		System.out.println("zdt="+zdt); 
	


3. 新转旧

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

import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Demo25 

	public static void main(String[] args) 
		//新版API中的ZonedDateTime,先转为long类型
		ZonedDateTime zdt = ZonedDateTime.now();
		//获取秒级时间戳,转为long类型
		long ts = zdt.toEpochSecond() * 1000;
		System.out.println("ts=" + ts);

		//然后将long类型,转为旧版API中的Date
		Date date = new Date(ts);
		System.out.println("date=" + date);
		
		//将long类型转为旧版API中的Calendar对象
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId()));
		calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
		System.out.println("calendar=" + calendar);
	



七. 结语

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

● 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型的时间戳做一个“中转”。


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

有技术类问题欢迎大家和我们一起交流讨论,更多技术类干货,关注千锋教育

Java8新特性之:新的日期和时间API

一. LocalDate、LocalTime、Instant、Duration以及Period

    

    1.LocalDate只提供日期,不含当天时间信息

LocalDate date = LocalDate.of(2018, 5, 03);
int year = date.getYear();
Month month = date.getMonth();
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();
int len = date.lengthOfMonth();
boolean leap = date.isLeapYear();
LocalDate today = LocalDate.now();
int year1 = date.get(ChronoField.YEAR);
int month1 = date.get(ChronoField.MONTH_OF_YEAR);
int day1 = date.get(ChronoField.DAY_OF_MONTH);



    2.LocalTime:一天中的时间

LocalTime time = LocalTime.of(13, 45, 20);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
LocalDate localDate = LocalDate.parse("2014-03-24");
LocalTime localTime = LocalTime.parse("14:34:22");



    3.LocalDateTime是LocalDate和LocalTime的合体

        同时表示了日期和时间,单不带有时区信息,可以直接创建也可以通过合并日期和时间对象构造。

LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();



    4.java.time.Instant类对时间建模的方式

        基本上是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。

        可以通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。

Instant instant1 = Instant.ofEpochSecond(3);
Instant instant2 = Instant.ofEpochSecond(3, 0);
Instant instant3 = Instant.ofEpochSecond(2, 1_000_000);//2秒之后再加上100万纳秒(1秒)
Instant instant4 = Instant.ofEpochSecond(4, -1_000_000);//4秒之前的100万纳秒(1秒)



以上类都实现了Temporal接口。Temporal接口定义了如何读取和操纵为时间建模的对象的值。

    5.Duration.between()两个对象之间的间隔

Duration d1 = Duration.between(time, time1);
Duration d2 = Duration.between(dt1, dt2);
Duration d3 = Duration.between(instant1, instant2);



    6.Period类:以年、月或者日的方式对多个时间单位建模。

Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
Duration threeMinutes = Duration.ofMinutes(3);
Duration fourMinutes = Duration.of(4, ChronoUnit.MINUTES);
Period elevenDays = Period.ofDays(11);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);


    日期-时间类中表示时间间隔的通用方法:

方法名

是否是静态方法

方法描述

between

创建两个时间点之间的interval

from

由一个临时时间创建的interval

of

由它的组成部分创建的interval的实例

parse

由字符串创建的interval的实例

addTo

创建该interval的副本,并将其叠加到某个指定的temporal对象

get

读取该interval的状态

isNegative

检查该interval是否为负值,不包含零

isZero

检查该interval的时长是否为零

minus

通过减去一定的时间创建该interval的副本

multipliedBy

将interval的值乘以某个标量创建该interval的副本

negated

以忽略某个时长的方式创建该interval的副本

plus

以增加某个指定的时长的方式创建该interval的副本

subtractFrom

从指定的temporal对象中减去该interval

二. 操纵、解析和格式化

    1.LocalDate、LocalTime、LocalDateTime、Instant表示时间点的日期-时间类的通用方法:

    他们都不会修改原来的对象。

方法名

是否静态方法

描述

from

依据传入的Temporal对象创建对象实例

now

依据系统时钟创建Temporal

of

由Temporal对象的某个部分创建该对象的实例

parse

由字符串创建Temporal对象的实例

atOffset

将Temporal对象和某个时区偏移相结合

atZone

将Temporal对象和某个时区相结合

format

使用某个指定的格式器将Temporal对象转换为字符串(Instant类不提供改方法)

get

读取Temporal对象的某一部分的值

minus

创建Temporal对象的一个副本,通过将当前Temporal对象的值减去一定的时长创建该副本

plus

创建Temporal对象的一个副本,通过将当前Temporal对象的值加上一定的时长创建该副本

with

以该Temporal对象为模板,对某些状态进行修改创建该对象的副本

        2.TemporalAdjuster类中的工厂方法

方法名

描述

dayOfWeekInMonth

创建一个新的日期,它的值为同一个月中每一周的第几天

firstDayOfMonth

创建一个新的日期,它的值为当月的第一天

firstDayOfNextMonth

创建一个新的日期,它的值为下月的第一天

firstDayOfNextYear

创建一个新的日期,它的值为明年的第一天

firstDayOfYear

创建一个新的日期,它的值为当年的第一天

firstInMonth

创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值

lastDayOfMonth

创建一个新的日期,它的值为当月的最后一天

lastDayOfNextMonth

创建一个新的日期,它的值为下的最后一天

lastDayOfNextYear

创建一个新的日期,它的值为明年的最后一天

lastDayOfYear

创建一个新的日期,它的值为今年的最后一天

lastInMonth

创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值

next/previous

创建一个新的日期,并将其值设定为日期调整后或调整前,第一个符合指定星期几要求的日期

nextOrSame/previousOrSame

创建一个新的日期,并将其值设定为日期调整后或调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象

    3.打印输出及解析日期-时间对象

        DateTimeFormatter:格式化及解析日期-时间对象。

        

        与老的DateFormat相比较,DateTimeFormatter实例都是线程安全的。所以能够以单例模式创建格式器实例。

LocalDate date2 = LocalDate.of(2019, 4, 24);
String s1 = date2.format(DateTimeFormatter.BASIC_ISO_DATE);//20190424
String s2 = date2.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-04-24
LocalDate date3 = LocalDate.parse("20190424", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date4 = LocalDate.parse("2019-04-24", DateTimeFormatter.ISO_LOCAL_DATE);


        DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器

DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(". ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN)


   

三. 处理不同的时区和历法

    

    ZoneId:地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供

ZoneId romeZone = ZoneId.of("Europe/Rome");


    ZoneId对象可以与LocalDate、LocateDateTime或者是Instant对象整合起来,构造为一个ZoneDateTime实例,它代表了相对于指定时区的时间点。

LocalDate date7 = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date7.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);




以上是关于JDK8 新特性之新的日期&时间API,一篇讲清楚!的主要内容,如果未能解决你的问题,请参考以下文章

JDK8的新特性

2020了你还不会Java8新特性?时间日期API&&Java8总结

jdk1.8新特性

Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的特点

JDK8 新特性

JDK8之时间日期处理类