Java日期类API总结(日期加减、日期比较、源码+注释)

链接导航

源码地址:码云
语雀同步:美观

学习目标

● 掌握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]

各个类封装时间所表示的特点

  1. Instant封装的时间为祖鲁时间并非当前时间.
    祖鲁时间也是格林尼治时间,也就是国际标准时间.
  2. LocalDate封装的只有年月日,没有时分秒,格式为yyyy-MM-dd.
  3. LocalTime封装的只有时分秒,没有年月日,格式为hh:mm:ss.sss,最
    后的sss是纳秒.
  4. LocalDateTime将LocalDate和LocalTime合二为一,在年月日与时分
    秒中间使用T作为分隔.
  5. 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添加进 去,就可以查看到了,方式如下.

  1. 封装时间LocalDateTime并添加时区信息.
  2. 更改时区信息查看对应时间.
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中的使用

  1. LocalDate中定义了多种对日期进行增减操作的方法
    ● LocalDate plusDays(long days) 增加天数
    ● LocalDate plusWeeks(long weeks) 增加周数
    ● LocalDate plusMonths(long months) 增加月数
    ● LocalDate plusYears(long years) 增加年数
  2. 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年.

  1. 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类中常用静态方法的使用

  1. static TemporalAdjuster firsyDayOfNextMonth()
    下个月的第一天
  2. static TemporalAdjuster firstDayOfNextYear()
    下一年的第一天
  3. static TemporalAdjuster firstDayOfYear()
    当年的第一天
  4. static TemporaAdjuster firstInMonth(DayOfWeek dayOfWeek)
    当月的第一个周x(通过参数确定)
  5. static TemporaAdjuster lastDayOfMonth()
    当月的最后一天
  6. static TemporaAdjuster lastDayOfYear()
    当年的最后一天
  7. static TemporaAdjuste lastInMonth(DayOfWeek dayOfWeek)
    当月的最后一个周x(通过参数确定)
  8. static TemporaAdjuster next(DayOfWeek dayOfWeek)
    下一个周x(通过参数确定)
  9. 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类接口中的方式来完成.

  1. 创建类实现TemporalAdjuster接口
  2. 实现TemporalAdjuster中的adjustInto方法,传入一个日期时间对象,完成逻辑之后返回日 期时间对象.
  3. 通过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);
    }
}

控制台

预计的发薪日是20181215,实际的发薪日为: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:202242日 星期六
LONG:202242日
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
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值