文章目录
- 链接导航
- 学习目标
- Date-Time API中的基本类使用
- 对日期与时间对象的加减
- 调节器TemporalAdjuster与查询TemporalQuery
- java.util.Date与java.time.LocalDate的转换
- 日期的解析与格式化DateTimeFormatter
链接导航
源码地址:码云
 语雀同步:美观
学习目标
● 掌握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代表中国是东八区,时间比国际标准时间快八小时.
● 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);
    }
}
对日期与时间对象的加减
想要修改某个日期/时间对象的现有实例时,我们可以使用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 + "天.");
    }
}
控制台
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










