文章目录
链接导航
学习目标
● 掌握Java8中的提供的java.time包中的常用日期类与相关方法
● 可以从java.util包的下的日期类相关类过渡到java.time包下的日期类
● 掌握Java8中的日期与字符串之间的相互转换
Date-Time API中的基本类使用
常用类的概述与功能介绍
- Instant类
Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,在之
后学习的类型转换中,均可以使用Instant类作为中间类完成转换. - Duration类
Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性. - Period类
Period类表示一段时间的年、月、日. - LocalDate类
LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日. - LocalTime类
LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表
示为纳秒精度. - LocalDateTime类
LocalDateTime是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日-
时-分-秒. - ZonedDateTime类
ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精
度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。
now方法在日期/时间类的使用
Date-Time API中的所有类均生成不可变实例,它们是线程安全的,并且这些类
不提供公共构造函数,也就是说没办法通过new的方式直接创建,需要采用工厂方法加以实例化.
public class Java8TimeClassMethodDemo1 {
public static void main(String[] args) {
//使用now方法创建Instant的实例对象.
Instant instantNow = Instant.now();
// 使用now方法创建LocalDate的实例对象.
LocalDate localDateNow = LocalDate.now();
// 使用now方法创建LocalTime的实例对象.
LocalTime localTimeNow = LocalTime.now();
// 使用now方法创建LocalDateTime的实例对象.
LocalDateTime localDateTimeNow = LocalDateTime.now();
// 使用now方法创建ZonedDateTime的实例对象.
ZonedDateTime zonedDateTimeNow = ZonedDateTime.now();
// 将实例对象打印到控制台.
System.out.println("Instant:"+instantNow);
System.out.println("LocalDate:"+localDateNow);
System.out.println("LocalTime:"+localTimeNow);
System.out.println("LocalDateTime:"+localDateTimeNow);
System.out.println("ZonedDateTime:"+zonedDateTimeNow);
}
}
输出结果控制台
Instant:2022-04-01T12:35:04.780Z
LocalDate:2022-04-01
LocalTime:20:35:04.884
LocalDateTime:2022-04-01T20:35:04.885
ZonedDateTime:2022-04-01T20:35:04.885+08:00[Asia/Shanghai]
各个类封装时间所表示的特点
- Instant封装的时间为祖鲁时间并非当前时间.
祖鲁时间也是格林尼治时间,也就是国际标准时间. - LocalDate封装的只有年月日,没有时分秒,格式为yyyy-MM-dd.
- LocalTime封装的只有时分秒,没有年月日,格式为hh:mm:ss.sss,最
后的sss是纳秒. - LocalDateTime将LocalDate和LocalTime合二为一,在年月日与时分
秒中间使用T作为分隔. - ZonedDateTime中封装了年月日时分秒,以及UTC(祖鲁时间)偏移量,并
且还有一个地区名.
+8:00代表中国是东八区,时间比国际标准时间快八小时.
不仅仅是刚才提供的几个类可以使用now方法,Java8的Time包中还提供了其他的几个类可以更精 准的获取某些信息.
● Year类(表示年)
● YearMonth类(表示年月)
● MonthDay类(表示月日)
public class Java8TimeClassMethodDemo2 {
public static void main(String[] args) {
//初始化Year的实例化对象.
Year year = Year.now();
//初始化YearMonth的实例化对象
YearMonth month = YearMonth.now();
//初始化MonthDay的实例化对象.
MonthDay day = MonthDay.now();
System.out.println(year);
System.out.println(month);
System.out.println(day);
}
}
控制台输出结果
2022
2022-04
--04-01
of方法自定义日期/时间类的应用
of方法可以根据给定的参数生成对应的日期/时间对象,基本上每个基本类都有
of方法用于生成的对应的对象,而且重载形式多变,可以根据不同的参数生成对
应的数据.
package com.liugx.dateapi;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Java8TimeClassMethodDemo3 {
public static void main(String[] args) {
//初始化2018年8月8日的LocalDate对象.
LocalDate date = LocalDate.of(2018, 8, 8);
System.out.println("LocalDate:" + date);
/*
初始化晚上7点0分0秒的LocalTime对象.
LocalTime.of方法的重载形式有以下几种,可以根据实际情况自行使用.
LocalTime of(int hour, int minute) -> 根据小时/分钟生成对象.
LocalTime of(int hour, int minute, int second) -> 根据小时/分钟/秒生成
对象.
LocalTime of(int hour, int minute, int second, int nanoOfSecond) ->
根据小时/分钟/毫秒/纳秒生成对象.
注意:如果秒和纳秒为0的话,那么默认不会封装这些数据,只显示小时和分钟.
*/
LocalTime time = LocalTime.of(19, 0, 0, 0);
System.out.println("LocalTime:" + time);
/*为LocalDateTime添加时区信息(拓展)
在学习ZonedDateTime的时候,发现了这个对象里面封装的不仅有时间日期,并且还有偏移量+时
区,那么时区如何在Java中获取呢,通过提供的一个类ZoneId的getAvailableZoneIds方法可以
获取到一个Set集合,集合中封装了600个时区.
初始化2018年8月8日下午7点0分的LocalDateTime对象.
LocalDateTime.of方法的重载形式有以下几种,可以根据事情自行使用.
LocalDateTime of(int year, int month, int dayOfMonth, int hour, int
minute, int second, int nanoOfSecond) -> 根据年/月/日/时/分/秒生成对象.
LocalDateTime of(int year, int month, int dayOfMonth, int hour, int
minute) -> 根据年/月/日/时/分生成对象.
注意:LocalDateTime of(LocalDate date, LocalTime time)方法可以将一个
LocalDate对象和一个LocalTime对象合并封装为一个LocalDateTime对象.
*/
LocalDateTime.of(2018, 8, 8, 19, 0, 0, 0);
LocalDateTime localDateTime = LocalDateTime.of(date, time);
System.out.println("LocalDateTime:" + localDateTime);
}
}
控制台输出结果
LocalDate:2018-08-08
LocalTime:19:00
LocalDateTime:2018-08-08T19:00
为LocalDateTime添加时区信息
在学习ZonedDateTime的时候,发现了这个对象里面封装的不仅有时间日期,并且还有偏移量+时区,那么时区如何在Java中获取呢,通过提供的一个类ZoneId的getAvailableZoneIds方法可以 获取到一个Set集合,集合中封装了600个时区.
public static void main(String[] args) {
//获取所有的时区信息
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
for (String zoneId : availableZoneIds) {
System.out.println(zoneId);
}
}
控制台
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
等等.....
同样也提供了获取当前系统默认的时区的方式systemDefault()方法
//获取当前系统默认的时区信息
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
//输出结果 Asia/Shanghai
我们可以通过给LocalDateTime添加时区信息来查看到不同时区的时间,比如说LocalDateTime中当前封装的是上海时间,那么想知道在此时此刻,纽约的时间是什么,就可以将纽约的时区Id添加进 去,就可以查看到了,方式如下.
- 封装时间LocalDateTime并添加时区信息.
- 更改时区信息查看对应时间.
public static void main(String[] args) {
//1.封装LocalDateTime对象,参数自定义 -> 2018年11月11日 8点54分38秒
LocalDateTime time = LocalDateTime.of(2018, 11, 11, 8, 54, 38);
//2.封装完成后的time对象只是封装的是一个时间,并没有时区相关的数据,所以添加时区到 对象中,使用atZone方法.
ZonedDateTime zonedDateTime = time.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("Asia/Shanghai的时间是:" + zonedDateTime);
//3.更改时区查看其它时区的当前时间,通过withZoneSameInstant方法即可更改.
ZonedDateTime otherZonedTime =
zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("在同一时刻,Asia/Tokyo的时间是:" + otherZonedTime);
}
控制台输出
Asia/Shanghai的时间是:2018-11-11T08:54:38+08:00[Asia/Shanghai]
在同一时刻,Asia/Tokyo的时间是:2018-11-11T09:54:38+09:00[Asia/Tokyo]
Month枚举类的使用
java.time包中引入了Month的枚举,Month中包含标准日历中的12个月份的常
量(从JANURAY到DECEMEBER)也提供了一些方便的方法供我们使用.
推荐在初始化LocalDate和LocalDateTime对象的时候,月份的参数使用枚举的
方式传入,这样更简单易懂而且不易出错,因为如果是老的思维,Calendar传入0
的话,那么会出现异常
import java.time.LocalDateTime;
import java.time.Month;
/**
* Month枚举类的使用.
*/
public class Java8TimeClassMethodDemo6 {
public static void main(String[] args) {
//在初始化LocalDate和LocalDateTime的时候,月份的参数传入枚举类(2011年5月15日11时11分11秒)
LocalDateTime.of(2011, Month.JUNE,15,11,11,11);
//of方法可以根据传入的数字返回对应的月份.
Month month = Month.of(12);
System.out.println(month);
}
}
输出结果:DECEMBER
对日期与时间对象的加减
想要修改某个日期/时间对象的现有实例时,我们可以使用plus和minus方法来完成操作.
Java8中日期时间相关的API中的所有实例都是不可改变的,一旦创建LocalDate,LocalTime,LocalDateTime就无法修改他们(类似于String),这对于线程安全非常有利.
plus方法在LocalDate与LocalTime中的使用
- LocalDate中定义了多种对日期进行增减操作的方法
● LocalDate plusDays(long days) 增加天数
● LocalDate plusWeeks(long weeks) 增加周数
● LocalDate plusMonths(long months) 增加月数
● LocalDate plusYears(long years) 增加年数 - LocalTime中定义了多种对时间进行增减操作的方法
● LocalTime plusNanos(long nanos) 增加纳秒
● LocalTime plusSeconds(long seconds) 增加秒
● LocalTime plusMinutes(long minutes) 增加分钟
● LocalTime plusHours(long hours) 增加小时
LocalDate日期的加减
import java.time.LocalDate;
import java.time.Month;
public class Java8TimeMethodPlusDemo1 {
public static void main(String[] args) {
//封装LocalDate对象参数为2016年2月13日.
LocalDate date = LocalDate.of(2016, Month.FEBRUARY, 13);
//计算当前时间的4天后的时间.
LocalDate plusDaysTime = date.plusDays(4);
//计算当前时间的3周后的时间.
LocalDate plusWeeksTime = date.plusWeeks(3);
//计算当前时间的5个月后的时间.
LocalDate plusMonthsTime = date.plusMonths(5);
//计算当前时间的2年后的时间.
LocalDate plusYearsTime = date.plusYears(2);
System.out.println("当前的时间是:"+date);
System.out.println("4天后的时间是:"+plusDaysTime);
System.out.println("3周后的时间是:"+plusWeeksTime);
System.out.println("5个月后的时间是:"+plusMonthsTime);
System.out.println("2年后的时间是:"+plusYearsTime);
}
}
控制台输出结果
当前的时间是:2016-02-13
4天后的时间是:2016-02-17
3周后的时间是:2016-03-05
5个月后的时间是:2016-07-13
2年后的时间是:2018-02-13
LocalTime时间的加减
import java.time.LocalTime;
public class Java8TimeMethodPlusDemo2 {
public static void main(String[] args) {
//封装LocalTime对象参数为8时14分39秒218纳秒.
LocalTime time = LocalTime.of(8, 14, 39, 218);
//计算当前时间500纳秒后的时间.
LocalTime plusNanosTime = time.plusNanos(500);
//计算当前时间45秒后的时间.
LocalTime plusSecondsTime = time.plusSeconds(45);
//计算当前时间19分钟后的时间.
LocalTime plusMinutesTime = time.plusMinutes(19);
//计算当前时间3小时后的时间.
LocalTime plusHoursTime = time.plusHours(3);
System.out.println("当前的时间是:" + time);
System.out.println("45秒后的时间是:" + plusSecondsTime);
System.out.println("19分钟后的时间是:" + plusMinutesTime);
System.out.println("500纳秒后的时间是:" + plusNanosTime);
System.out.println("3小时后的时间是:" + plusHoursTime);
}
}
控制台输出结果
当前的时间是:08:14:39.000000218
45秒后的时间是:08:15:24.000000218
19分钟后的时间是:08:33:39.000000218
500纳秒后的时间是:08:14:39.000000718
3小时后的时间是:11:14:39.000000218
本文中都是使用plusXXX的方法进行演示,实际上也有对应的减少
方法,以minus开头的方法对应的即为减少,实际上minus方法调用
的也是plus方法,只不过传入的参数是负数.
plus和minus方法的应用
刚才学习到的plusXXX相关的方法都是添加了数值到具体的某一项上,根据观察
还有两个单独的plus方法,接下来我们来学习这两个单独的plus方法.
plus(TemporaAmount amountToAdd)
TemporaAmount是一个接口,当接口作为方法的参数的时候,实际上传入的是接口的实现
类对象,根据查看这个接口的体系,可以看到这个接口有一个实现类,名字叫做Period,
在学习第一节的时候,说明了这个类表示一段时间.
如何使用Period来表示一段时间呢?这个类本身提供了of(int year,int month,int
day)来表示,例:Period.of(1,2,3)返回的对象表示的即为1年2个月3天
这么一个时间段,我们可以使用plus方法添加这个时间段.
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;
/*
今天程序员小郝在查看自己的车辆保险记录的时候查看到还有2年3个月零8天保险就到期了,计算2年3个月零8天后的时间是多少.
*/
public class Java8TimeMethodPlusDemo4 {
public static void main(String[] args) {
LocalDate date = LocalDate.now(); //date表示当前时间.
//固然可以使用对于年月日依次+2,+3,+8的方式来操作,但是有些繁琐,首先我们先将2年3月8天封装为一段时间,也就是封装为一个Period对象.
Period time = Period.of(2, 3, 8);
//使用plus方法对于date对象直接进行增加的操作.
LocalDate endDate = date.plus(time);
System.out.println("今天是" + date + ",保险到期的时间是" + endDate + ".");
LocalDate plus = date.plus(1, ChronoUnit.CENTURIES);
System.out.println(plus);
}
}
控制台输出结果
今天是2022-04-01,保险到期的时间是2024-07-09.
2122-04-01
plus(long l,TemporaUnit unit)
在实际开发过程中,可能还会更精准的去操作日期或者说增加一些特殊的时间,比如说1
个世纪,1个半天,1千年,10年等,Java8提供了这些日期的表示方式而不需要去单独进行
计算了.
TemporaUnit是一个接口,通过查看体系接口发现,可以使用子类ChronoUnit来表
示,ChronoUnit封装了很多时间段供我们使用.
import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;
/*
结婚10年称为锡婚,2020年2月2日11点11分11秒称为对称日,很多情侣准备在那天结婚,如果在那天结婚了,那么锡婚会发生在什么时候.
*/
public class Java8TimeMethodPlusDemo5 {
public static void main(String[] args) {
LocalDateTime marryTime = LocalDateTime.of(2020, Month.FEBRUARY, 2, 11, 11, 11);
//使用plus方法进行计算,添加1个,ChronoUnit.DECADES(十年).
LocalDateTime time = marryTime.plus(1, ChronoUnit.DECADES);
System.out.println("如果在" + marryTime + "结婚,那么锡婚是" + time);
//如果锡婚后的半天准备要请所有亲戚朋友吃饭,那么吃饭的时间是.
LocalDateTime eatTime = time.plus(1, ChronoUnit.HALF_DAYS);
System.out.println("半天后吃饭,吃饭的时候是:" + eatTime);
}
}
控制台
如果在2020-02-02T11:11:11结婚,那么锡婚是2030-02-02T11:11:11
半天后吃饭,吃饭的时候是:2030-02-02T23:11:11
注意第一个参数为单位,第二个参数为时间长度.
例:plus(1, ChronoUnit.DECADES)加1个10年.
plus(1, ChronoUnit.CENTURIES)加1个100年.
- with方法在LocalDateTime类的应用
如果不需要对日期进行加减而是要直接修改的话,那么可以使用with方法,with方法提供了很多种修改时间的方式.
● LocalDateTime withNano(int i) 修改纳秒
● LocalDateTime withSecond(int i) 修改秒
● LocalDateTime withMinute(int i) 修改分钟
● LocalDateTime withHour(int i) 修改小时
● LocalDateTime withDayOfMonth(int i) 修改日
● LocalDateTime withMonth(int i) 修改月
● LocalDateTime withYear(int i) 修改年
withDayOfMonth(int dayOfMonth)
import java.time.LocalDateTime;
public class Java8TimeMethodWithDemo1 {
public static void main(String[] args) {
LocalDateTime time = LocalDateTime.now();
//经过使用发现time中的时间有错误,应该是1日,在不知道原有时间的基础上,无法进行增减操作,所以可以直接使用with方法进行修改.
LocalDateTime endTime = time.withDayOfMonth(1);
System.out.println("修改前错误的时间是:" + time);
System.out.println("修改完成之后的时间是:" + endTime);
}
}
控制台
修改前错误的时间是:2022-04-01T23:05:20.061
修改完成之后的时间是:2022-04-01T23:05:20.061
with(TemporalField field, long newValue)
TemporalField是一个接口,通过查看体系结构,可以使用它的子类
ChronoField,ChronoField中封装了一些日期时间中的组成部分,可以直接选择之后传
入第二个参数进行修改.
例:with(ChronoField.DAY_OF_MONTH,1); 将日期中的月份中的天数改为1.
例:with(ChronoField.YEAR,2021); 将日期中的年份改为2021.
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
public class Java8TimeMethodWithDemo2 {
public static void main(String[] args) {
LocalDateTime time = LocalDateTime.now();
//经过使用发现time中的时间有错误,应该是1日,在不知道原有时间的基础上,无法进行增减操作,所以可以直接使用with方法进行修改.
LocalDateTime endTime = time.with(ChronoField.DAY_OF_MONTH,3);
System.out.println("修改前错误的时间是:" + time);
System.out.println("修改完成之后的时间是:" + endTime);
}
}
控制台
修改前错误的时间是:2022-04-01T23:12:03.474
修改完成之后的时间是:2022-04-03T23:12:03.474
调节器TemporalAdjuster与查询TemporalQuery
在with方法中学习了可以通过with方法修改日期时间对象中封装的数据,但是有一些时候可能会做一些复杂的操作,比如说将时间调整到下个周的周日,下 一个工作日,或者本月中的某一天,这个时候可以使用调节器TemporalAdjuster来 更方便的处理日期.
with方法有一个重载形式,需要传入一个TemporalAdjuster对象,通过查看发现TemporalAdjuster是 一个接口,方法的参数是一个接口,那么实际上传入的是这个接口的实现类对象.
TemporalAdjusters的类可以给我们提供一些常用的方法,方法如下.
TemporalAdjusters类中常用静态方法的使用
- static TemporalAdjuster firsyDayOfNextMonth()
下个月的第一天 - static TemporalAdjuster firstDayOfNextYear()
下一年的第一天 - static TemporalAdjuster firstDayOfYear()
当年的第一天 - static TemporaAdjuster firstInMonth(DayOfWeek dayOfWeek)
当月的第一个周x(通过参数确定) - static TemporaAdjuster lastDayOfMonth()
当月的最后一天 - static TemporaAdjuster lastDayOfYear()
当年的最后一天 - static TemporaAdjuste lastInMonth(DayOfWeek dayOfWeek)
当月的最后一个周x(通过参数确定) - static TemporaAdjuster next(DayOfWeek dayOfWeek)
下一个周x(通过参数确定) - static TemporaAdjuster previous(DayOfWeek dayOfWeek)
上一个周x(通过参数确定)
TemporalAdjuster是一个函数式接口,里面有一个抽象方法叫做Temporal
adjustInto(Temporal temporal);传入一个Temporal对象通过实现逻辑返回
一个Temporal对象,Temporal是LocalDate,LocalTime相关日期类的父接口,
实际上传入的就是一个时间日期对象返回一个时间日期对象.
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class Java8TimeTemporalAdjusterDemo1 {
public static void main(String[] args) {
//封装日期时间对象为当前时间,LocalDate.
LocalDate time = LocalDate.now();
/*
with方法可以修改time对象中封装的数据,需要传入一个TemporalAdjuster对象,
通过查看发现TemporalAdjuster是一个接口,方法的参数是一个接口,那么实际上传入的是这个接口的实现类对象.
TemporalAdjusters的类可以给我们提供一些常用的方法.
*/
//with方法传入了TemporalAdjuster类的实现对象,是由TemporalAdjusters类的方法实现了adjustInto方法,当前的逻辑是:将时间修改为当月的第一天.
LocalDate firstDayOfMonth = time.with(TemporalAdjusters.firstDayOfMonth());
//将时间修改为下个月的第一天.
LocalDate firstDayOfNextMonth = time.with(TemporalAdjusters.firstDayOfNextMonth());
//将时间修改为下一年的第一天.
LocalDate firstDayOfNextYear = time.with(TemporalAdjusters.firstDayOfNextYear());
//将时间修改为本年的第一天.
LocalDate firstDayOfYear = time.with(TemporalAdjusters.firstDayOfYear());
//将时间修改为本月的最后一天.
LocalDate lastDayOfMonth = time.with(TemporalAdjusters.lastDayOfMonth());
//将时间修改为本年的最后一天.
LocalDate lastDayOfYear = time.with(TemporalAdjusters.lastDayOfYear());
System.out.println("当月的第一天是:" + firstDayOfMonth);
System.out.println("下个月的第一天是:" + firstDayOfNextMonth);
System.out.println("下一年的第一天是:" + firstDayOfNextYear);
System.out.println("本年的第一天是:" + firstDayOfYear);
System.out.println("本月的最后一天是:" + lastDayOfMonth);
System.out.println("本年的最后一天是:" + lastDayOfYear);
}
}
控制台
当月的第一天是:2022-04-01
下个月的第一天是:2022-05-01
下一年的第一天是:2023-01-01
本年的第一天是:2022-01-01
本月的最后一天是:2022-04-30
本年的最后一天是:2022-12-31
DayOfWeek的使用
DayOfWeek是一周中星期几的枚举类,其中封装了从周一到周日.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class Java8TimeTemporalAdjusterDemo2 {
public static void main(String[] args) {
//封装日期时间对象为当前时间,LocalDate.
LocalDate time = LocalDate.now();
/*
DayOfWeek是一周中星期几的枚举类,其中封装了从周一到周日.
*/
//将当前时间修改为下一个周日
LocalDate nextSunday = time.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
//将当前时间修改为上一个周三
LocalDate previousWednesday = time.with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));
System.out.println("下一个周日是:"+nextSunday);
System.out.println("上一个周三是:"+previousWednesday);
}
}
控制台
下一个周日是:2022-04-03
上一个周三是:2022-03-30
自定义TemporalAdjuster调节器
通过Java8本身提供的TemporalAdjusters中的方法可以完成一些常用的操作,如果要自定义日期时间的更改逻辑,可以通过实现TemporalAdjuster类接口中的方式来完成.
- 创建类实现TemporalAdjuster接口
- 实现TemporalAdjuster中的adjustInto方法,传入一个日期时间对象,完成逻辑之后返回日 期时间对象.
- 通过from方法传入自定义调节器对象完成更改.
例:假如员工一个月中领取工资,发薪日是每个月的15号,如果发薪日是周末,则调整为周五.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.*;
/**
* 假如员工一个月中领取工资,发薪日是每个月的15号,如果发薪日是周末,则调整为周五.
*/
public class PayDayAdjuster implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
//1.将temporal转换为子类对象LocalDate,from方法可以将任何时态对象转换为LocalDate.
LocalDate payDay = LocalDate.from(temporal);
//2.判断当前封装的时间中的日期是不是当月15日,如果不是,则更改为15日.
int day;
if (payDay.getDayOfMonth() != 15) {
day = 15;
} else {
day = payDay.getDayOfMonth();
}
LocalDate realPayDay = payDay.withDayOfMonth(day);
//3.判断realPayDay对象中封装的星期数是不是周六或者是周日,如果是周末或者是周日则更改为周五.
if (realPayDay.getDayOfWeek() == DayOfWeek.SUNDAY || realPayDay.getDayOfWeek() == DayOfWeek.SATURDAY) {
//说明发薪日是周末,则更改为周五.
realPayDay = realPayDay.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}
return realPayDay;
}
}
import java.time.LocalDate;
public class Java8TimeTemporalAdjusterTest1 {
public static void main(String[] args) {
//封装LocalDate对象为2018年12月1日.
LocalDate payDay = LocalDate.of(2019, 12, 1);
//2018年12月15日为周末,所以要提前到周五发放工资,通过自定义调节器完成对时间的修改.
LocalDate realPayDay = LocalDate.from(new PayDayAdjuster().adjustInto(payDay));
System.out.println("预计的发薪日是2018年12月15日,实际的发薪日为:" + realPayDay);
}
}
控制台
预计的发薪日是2018年12月15日,实际的发薪日为:2019-12-13
TemporalQuery的应用
学习的时态类对象(LocalDate,LocalTime)都有一个方法叫做query,可以针对日期进行查询.
R query(TemporalQuery query)这个方法是一个泛型方法,返回的数据就是传入的泛型类的类 型,TemporalQuery是一个泛型接口,里面有一个抽象方法是
R queryFrom(TemporalAccessor temporal),TemporalAccessor是Temporal的父接口,实际
上也就是LocalDate,LocalDateTime相关类的顶级父接口,这个queryFrom的方法的实现逻辑就
是,传入一个日期/时间对象通过自定义逻辑返回数据.
如果要计划日期距离某一个特定天数差距多少天,可以自定义类实现TemporalQuery接口并且作为参数传入到query方法中.
例:计算当前时间距离下一个劳动节还有多少天?
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
/**
* 获取某一天距离下一个劳动节的相隔天数的实现类.
*/
public class UntilDayQueryImpl implements TemporalQuery<Long> {
@Override
public Long queryFrom(TemporalAccessor temporal) {
//获取当前的年/月/日信息.
int year = temporal.get(ChronoField.YEAR);
int month = temporal.get(ChronoField.MONTH_OF_YEAR);
int day = temporal.get(ChronoField.DAY_OF_MONTH);
//将获取到的数据封装为一个LocalDate对象.
LocalDate time = LocalDate.of(year, month, day);
//封装劳动节的时间,年参数传递year,month和day是5和1.
LocalDate laborDay = LocalDate.of(year, Month.MAY,1);
//判断当前时间是否已经超过了当年的劳动节,如果超过了,则laborDay+1年.
if (time.isAfter(laborDay)){
laborDay = laborDay.plusYears(1);
}
//通过ChronoUnit的between方法计算两个时间点的差额.
long l = ChronoUnit.DAYS.between(time, laborDay);
return l;
}
}
import java.time.LocalDate;
public class Java8TimeTemporalQueryDemo1 {
public static void main(String[] args) {
//封装LocalDate对象为当前时间.
LocalDate time = LocalDate.now();
//调用time对象的query方法查询距离下一个五一劳动节还有多少天.
Long l = time.query(new UntilDayQueryImpl());
System.out.println("距离下一个五一劳动节还有:" + l + "天.");
}
}
控制台
距离下一个五一劳动节还有:30天.
java.util.Date与java.time.LocalDate的转换
对于老项目的改造,需要将Date或者Calendar转换为java.time包中相应的类的,可
以根据本小节中提供的方法进行改造.
Java8中的java.time包中并没有提供太多的内置方式来转换java.util包中用预处
理标准日期和时间的类,我们可以使用Instant类作为中介,也可以使用
java.sql.Date和java.sql.Timestamp类提供的方法进行转换.
使用Instant类将java.util.Date转换为java.time.LocalDate
java.time包中并没有提供很多的方式来进行直接转换,但是给之前的Date类,Calendar类在
Java1.8都提供了一个新的方法,叫做toInstant,可以将当前对象转换为Instant对象,通过给
Instant添加时区信息之后就可以转换为LocalDate对象.
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
public class Java8TimeDateToLocalDateDemo1 {
public static void main(String[] args) {
//初始化Date对象.
Date d = new Date();
//将Date类对象转换为Instant类对象.
Instant i = d.toInstant();
//Date类包含日期和时间信息,但是并不提供时区信息,和Instant类一样,可以通过Instant类的atZone方法添加时区信息之后进行转换.
ZonedDateTime zonedDateTime = i.atZone(ZoneId.systemDefault());
//将ZonedDateTime通过toLocalDate方法转换为LocalDate对象.
LocalDate localDate = zonedDateTime.toLocalDate();
System.out.println("转换之前的Date对象是:" + d);
System.out.println("转换之后的LocalDate对象是:" + localDate);
}
}
控制台
转换之前的Date对象是:Fri Apr 01 23:47:53 CST 2022
转换之后的LocalDate对象是:2022-04-01
java.sql.Date类中的转换方法使用
java.sql.Date类中提供直接转换为LocalDate的方法,toLocalDate
public class Java8TimeDateToLocalDateDemo2 {
public static void main(String[] args) {
//初始化java.sql.Date对象.
Date d = new Date(System.currentTimeMillis());
//将java.sql.Date对象通过toLocalDate方法转换为LocalDate对象.
LocalDate localDate = d.toLocalDate();
System.out.println("转换前的java.sql.Date对象是:" + d);
System.out.println("转换后的LocalDate对象是:" + localDate);
}
}
控制台
转换前的java.sql.Date对象是:2022-04-01
转换后的LocalDate对象是:2022-04-01
java.sql.Timestamp类中的转换方法使用
TimeStamp是时间戳对象,通过传入一个毫秒值对象进行初始化.
import java.sql.Timestamp;
import java.time.LocalDateTime;
public class Java8TimeTimestampToLocalDateDemo1 {
public static void main(String[] args) {
//初始化java.sql.Timestamp对象.
Timestamp t = new Timestamp(System.currentTimeMillis());
//将java.sql.Timestamp对象通过toLocalDateTime方法转换为LocalDateTime对象.
LocalDateTime localDateTime = t.toLocalDateTime();
System.out.println("转换之前的Timestamp对象是:" + t);
System.out.println("转换之后的LocalDateTime对象是:" + localDateTime);
}
}
控制台
转换之前的Timestamp对象是:2022-04-01 23:54:33.397
转换之后的LocalDateTime对象是:2022-04-01T23:54:33.397
将java.util包中的类转换为java.time包中的相应类
通过编写转换工具类达到传入Date对象直接进行转换的转换或者将新的日期时间类转换为Date对 象.
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
/**
* 编写工具类传入不同的对象可以转换为对应的对象.
*/
public class Java8TimeConvertTool {
/**
* 将java.sql.Date转换为LocalDate
*
* @param date
* @return
*/
public static LocalDate convertFromSqlDateToLocalDate(java.sql.Date date) {
return date.toLocalDate();
}
/**
* 将LocalDate转换为java.sql.Date
* @param date
* @return
*/
public static java.sql.Date convertFromLocalDateToSqlDate(LocalDate date) {
return java.sql.Date.valueOf(date);
}
/**
* 将java.util.Date转换为LocalDate
* @param date
* @return
*/
public static LocalDate convertFromUtilDateToLocalDate(java.util.Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
/**
* 将java.sql.Timestamp转换为LocalDateTime
* @param timestamp
* @return
*/
public static LocalDateTime convertFromTimestampToLocalDateTime(java.sql.Timestamp timestamp) {
return timestamp.toLocalDateTime();
}
/**
* 将LocalDateTime转换为java.sql.Timestamp
* @param localDateTime
* @return
*/
public static java.sql.Timestamp convertFromLocalDateTimeToTimestamp(LocalDateTime localDateTime) {
return java.sql.Timestamp.valueOf(localDateTime);
}
/**
* 将LocalDate转换为java.util.Date
* @param date
* @return
*/
public static java.util.Date convertFromLocalDateToUtilDate(LocalDate date){
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault());
return Date.from(zonedDateTime.toInstant());
}
}
将java.util.Date类转换为java.time.LocalDate类的第二种方法
java.sql.Date类提供了转换为LocalDate的方法,那么可以将java.util.Date先转换为
java.sql.Date.
通过java.sql.Date的构造方法直接传入一个毫秒值可以构造一个java.sql.Date对象,毫秒值可
以通过java.util.Date对象的getTime方法获取到.
import java.time.LocalDate;
import java.util.Date;
public class Java8TimeDateToLocalDateDemo4 {
public static void main(String[] args) {
//初始化Date对象.
Date d = new Date();
/*
java.sql.Date类提供了转换为LocalDate的方法,那么可以将java.util.Date先转换为java.sql.Date.
通过java.sql.Date的构造方法直接传入一个毫秒值可以构造一个java.sql.Date对象,毫秒值可以通过java.util.Date对象的getTime方法获取到.
*/
java.sql.Date date = new java.sql.Date(d.getTime());
//将java.sql.Date转化为LocalDate.
LocalDate localDate = date.toLocalDate();
System.out.println("转换前的java.util.Date类对象是:" + d);
System.out.println("转换后的LocalDate类对象是:" + localDate);
}
}
控制台
转换前的java.util.Date类对象是:Fri Apr 01 23:59:51 CST 2022
转换后的LocalDate类对象是:2022-04-01
将java.util.Calendar类转换为java.time.ZonedDateTime类
Calendar对象自Java1.1开始提供了一个方法获取时区对象的方法,getTimeZone,要将Calendar 对象转换为ZonedDateTime需要先获取到时区对象.从Java1.8开始TimeZone类提供了一个方法可 以获取到ZonedId.获取到zoneId之后就可以初始化ZonedDateTime对了,ZonedDateTime类有 一个ofInstant方法,可以将一个Instant对象和ZonedId对象作为参数传入构造一个 ZonedDateTime对象.
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.TimeZone;
public class Java8TimeCalendarToZonedDateTimeDemo1 {
public static void main(String[] args) {
//初始化Canlendar对象.
Calendar cal = Calendar.getInstance();
//Calendar对象自Java1.1开始提供了一个方法获取时区对象的方法,getTimeZone,要将Calendar对象转换为ZonedDateTime需要先获取到时区对象.
TimeZone timeZone = cal.getTimeZone();
//从Java1.8开始TimeZone类提供了一个方法可以获取到ZonedId.
ZoneId zoneId = timeZone.toZoneId();
//获取到zoneId之后就可以初始化ZonedDateTime对象了,ZonedDateTime类有一个ofInstant方法,可以将一个Instant对象和ZonedId对象作为参数传入构造一个ZonedDateTime对象.
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(cal.toInstant(), zoneId);
System.out.println("转换前的Calendar对象是:" + cal);
System.out.println("转换后的ZonedDateTime对象是:" + zonedDateTime);
}
}
控制台
转换前的Calendar对象是:java.util.GregorianCalendar[time=1648829190731,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=14,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=92,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=6,SECOND=30,MILLISECOND=731,ZONE_OFFSET=28800000,DST_OFFSET=0]
转换后的ZonedDateTime对象是:2022-04-02T00:06:30.731+08:00[Asia/Shanghai]
将java.util.Calendar类转换为java.time.LocalDateTime类
Calendar对象可以获取到年月日时分秒的信息,这些信息可以作为LocalDateTime构造方法的参数.
import java.time.LocalDateTime;
import java.util.Calendar;
public class Java8TimeCalendarToLocalDateTimeDemo1 {
public static void main(String[] args) {
//初始化Canlendar对象.
Calendar cal = Calendar.getInstance();
//通过Getter方法获取到Calendar对象中封装的数据.
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DAY_OF_MONTH);
int hour = cal.get(Calendar.HOUR);
int minute = cal.get(Calendar.MINUTE);
int second = cal.get(Calendar.SECOND);
//将以上获取到的数据作为LocalDateTime的of方法的参数进行对象的封装.
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hour, minute, second);
System.out.println("转换前的Calendar对象是:" + cal);
System.out.println("转换后的LocalDateTime对象是:" + localDateTime);
}
}
控制台
转换前的Calendar对象是:java.util.GregorianCalendar[time=1648829003278,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=14,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=92,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=3,SECOND=23,MILLISECOND=278,ZONE_OFFSET=28800000,DST_OFFSET=0]
转换后的LocalDateTime对象是:2022-03-02T00:03:23
注意Calendar获取到的月份依旧是从0开始的,需要在原有的基础上+1,如果不
加1,轻则月份少算了1个月,重则出现异常.
日期的解析与格式化DateTimeFormatter
SimpleDateFormat类在刚开始的讲过了是线程不安全的,所以Java8提供了新的格式化类 DateTimeFormatter.
DateTimeFormatter类提供了大量预定义格式化器,包括常量(如
ISO_LOCAL_DATE),模式字母(如yyyy-MM-dd)以及本地化样式.
与SimpleDateFormat不同的是,新版本的日期/时间API的格式化与解析不需要在创
建转换器对象再进行转换了,通过时间日期对象的parse/format方法可以直接进行
转换.
LocalDate类定义的parse和format方法
@Override // override for Javadoc and performance
public String format(DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.format(this);
}
format方法需要传入一个DateTimeFormatter对象,实际上查看DateTimeFormatter类之后,指定 DateTimeFormatter中的常量即可指定格式化方法,常用的格式化方式有ISO_DATE_TIME/ISO_DATE.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Java8TimeFormatAndParseDemo1 {
public static void main(String[] args) {
//对LocalDateTime进行格式化与解析,初始化LocalDateTime对象.
LocalDateTime time = LocalDateTime.now();
//DateTimeFormatter类中定义了很多方式,通过常量名可以指定格式化方式.
String result = time.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println("ISO_DATE_TIME格式化之后的String是:" + result);
String result1 = time.format(DateTimeFormatter.ISO_DATE);
System.out.println("ISO_DATE格式化之后的String是:" + result1);
//解析字符串的方式通过LocalDateTime类的静态方法parse方法传入需要解析的字符串即可.
LocalDateTime localDateTime = LocalDateTime.parse(result);
System.out.println("解析了字符串之后的LocalDateTime是:" + localDateTime);
}
}
控制台
ISO_DATE_TIME格式化之后的String是:2022-04-02T00:12:23.697
ISO_DATE格式化之后的String是:2022-04-02
解析了字符串之后的LocalDateTime是:2022-04-02T00:12:23.697
对日期进行格式化
通过DateTimeFormatter的ofLocalizedDate的方法也可以调整格式化的方 式.
public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) {
Objects.requireNonNull(dateStyle, “dateStyle”);
return new DateTimeFormatterBuilder().appendLocalized(dateStyle, null)
.toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
}
此方法需要传入一个FormatStyle类对象,查看后发现FormatStyle对象是一个枚举类,其中有几
种方式如下.
Full:全显示(年月日+星期) Long:全显示(年月日) Medium:缩略显示(没有年
月日汉字) SHORT:精简显示(精简年+月日)
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class Java8TimeFormatAndParseDemo2 {
public static void main(String[] args) {
//对LocalDateTime进行格式化与解析,初始化LocalDateTime对象.
LocalDateTime time = LocalDateTime.now();
//通过DateTimeFormatter的ofLocalizedDate指定解析格式也可以格式化日期
String r1 = time.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
String r2 = time.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
String r3 = time.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
String r4 = time.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));
System.out.println("FULL:"+r1);
System.out.println("LONG:"+r2);
System.out.println("MEDIUM:"+r3);
System.out.println("SHORT:"+r4);
}
}
控制台
FULL:2022年4月2日 星期六
LONG:2022年4月2日
MEDIUM:2022-4-2
SHORT:22-4-2
注意此种方式在不同时区的显示方式不一样,在其他时区不会显示中文,会根据当前
系统的默认时区来进行区别显示.
自定义格式化格式
除了系统的自带的方式之外,也可以通过DateTimeFormatter类提供的
ofPattern方式创建自定时格式化器,格式化的写法与之前使用的
SimpleDateFormat相同.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class Java8TimeFormatAndParseDemo3 {
public static void main(String[] args) {
//对LocalDateTime进行格式化与解析,初始化LocalDateTime对象.
LocalDateTime time = LocalDateTime.now();
//通过通过DateTimeFormatter的ofPattern方法可以自定义格式化模式.
String result = time.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss:SSS"));
System.out.println("LocalDateTime格式化前是:" + time);
System.out.println("LocalDateTime格式化后是:" + result);
}
}
控制台
LocalDateTime格式化前是:2022-04-02T00:18:15.736
LocalDateTime格式化后是:2022/04/02 00:18:15:736