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
表示两个日期之间的间隔天数。
Duration
和Period
的表示方法也符合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
类来实现。但是当我们使用新的LocalDateTime
或ZonedDateTime
需要进行格式化显示时,就要使用新的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还提供了plusXxx
和minusXxx
增减方法,方便我们进行时间的操作。且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,一篇讲清楚!的主要内容,如果未能解决你的问题,请参考以下文章
2020了你还不会Java8新特性?时间日期API&&Java8总结