java8日期类详解

学习目标

  1. 掌握Java8中的提供的java.time包中的常用日期类与相关方法

  2. 可以从java.util包的下的日期类相关类过渡到java.time包下的日期类

  3. 掌握Java8中的日期与字符串之间的相互转换


为什么会出现新的日期类API

    将java.util.Date类束之高阁才是正确之道 -> Tim Yates
    在Java面世之初,标准库就引入了两种用于处理日期和时间的类,它们是 java.util.Date和java.util.Calendar,而前者堪称类糟糕设计的典范,浏览 API可以发现,从Java1.1开始,Date类中的所有方法就已经被弃用,Java1.1推荐采用Calendar类处理日期和时间,但是这个类同样存在不少问题.

对于日期的计算困难问题.

        毫秒值与日期直接转换比较繁琐,其次通过毫秒值来计算时间的差额步骤较多.

package com.itheima.time;
import java.util.Calendar;
import java.util.Date;
/**
* 计算当前时间距离2000年6月1日相差了多少天.
*
* 通过距离1970年1月1日的毫秒差值,可以计算出两个日期之间相隔的天数.
*/
public class JavaUtilTimeDemo01 {
  public static void main(String[] args) {
    //1.初始化Date对象,无参构造(无参构造默认代表的就是当前时间)
    Date dateNow = new Date();
    //2.获取当前时间的距离1970年1月1日过了多少毫秒.
    long dateTimeNow = dateNow.getTime();
    //3.初始化Calendar对象并设时间为2006年6月1日并且将Calendar对象转换为Date对象.
    Calendar paramterTime = Calendar.getInstance();
    paramterTime.set(2000, Calendar.JUNE, 1);
    //线程安全问题
    //SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个
    //SimpleDateFormat类中的Calendar对象有可能会出现异常.
    Date paramterDateTime = paramterTime.getTime();
    //4.计算paramterDateTime与dateTimeNow之间的毫秒差额.
    Long intervalTime = dateTimeNow -
    paramterDateTime.getTime();
    //5.对intervalTime进行计算获取差额,毫秒值/1000->/60->/60->/24
    long intervalDay = intervalTime / 1000 / 60 / 60 / 24;
    System.out.println("当前时间距离2000年6月1日已经过了" + intervalDay+"天.");
 }
}

线程安全问题

  SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个
  SimpleDateFormat类中的Calendar对象有可能会出现异常.

package com.itheima.time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class JavaUtilTimeDemo02 {
  //创建SimpleDateFormat的对象(单例)
  static SimpleDateFormat SIMPLEDATEFORMAT = new
SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  public static void main(String[] args) {
    //创建10个线程并启动.
    for (int i = 0; i < 10; i++) {
      new Thread(() -> {
        try {
        
           System.out.println(SIMPLEDATEFORMAT.parse("2018-12-12 12:12:12"));
       } catch (ParseException e) {
          e.printStackTrace();
       }
     }).start();
   }
 }
}

        另外的一个问题就是在java.util.Date和java.util.Calendar类之前,枚举类型(ENUM)还没有出现,所以在字段中使用整数常量导致整数常量都是可变的,而不是线程安全的.为了处理实际开发中遇到的问题,标准库随后引入了java.sql.Date作为java.util.Date的子类,但是还是没能彻底解决问题.最终JavaSE 8中引入了java.time包,这种全新的包从根本上解决了长久以来的存在的诸多弊,java.time包基于Joda-Time库构件,是一种免费的开源解决方案,实际上在Java 8没有出现之前,公司中已经广泛使用Joda-Time来解决Java中的日期与时间问题,Joda-Time的设计团队也参与了java.time包的开发.


Date-Time API中的基本类使用

 

常用类的概述与功能介绍

  • Instant类

 Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,在之后学习的类型转换中,均可以使用Instant类作为中间类完成转换.

  • Duration类

Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性.

  • Period类

Period类表示一段时间的年、月、日.

  • LocalDate类

LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日.

  • LocalTime类

LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度.

  • LocalDateTime类

LocalDateTime是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日-时-分-秒.

  • ZonedDateTime类

ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。

 

now方法在日期/时间类的使用

         Date-Time API中的所有类均生成不可变实例,它们是线程安全的,并且这些类不提供公共构造函数,也就是说没办法通过new的方式直接创建,需要采用工厂方法加以实例化.

         now方法可以根据当前日期或时间创建实例.

package com.itheima.time;
import java.time.*;

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封装的时间为祖鲁时间并非当前时间. 祖鲁时间也是格林尼治时间,也就是国际标准时间.
  • LocalDate封装的只有年月日,没有时分秒,格式为yyyy-MM-dd.
  • LocalTime封装的只有时分秒,没有年月日,格式为hh:mm:ss.sss,最后的sss是纳秒.
  • LocalDateTime将LocalDate和LocalTime合二为一,在年月日与时分秒中间使用T作为分隔.
  • ZonedDateTime中封装了年月日时分秒,以及UTC(祖鲁时间)偏移量,并且还有一个地区名.+8:00代表中国是东八区,时间比国际标准时间快八小时.

Java8 年 月 日 的枚举信息.

  1. Year类(表示年)
  2. YearMonth类(表示年月)
  3. MonthDay类(表示月日)
package com.itheima.time;

import java.time.*;
public class Java8TimeClassMethodDemo2 {
  public static void main(String[] args) {
    //初始化Year的实例化对象.
    Year year = Year.now();
    //初始化YearMonth的实例化对象
    YearMonth month = YearMonth.now();
    //初始化MonthDay的实例化对象.
    MonthDay day = MonthDay.now();
 }
}

of方法在日期/时间类的应用

     of方法可以根据给定的参数生成对应的日期/时间对象,基本上每个基本类都有of方法用于生成的对应的对象,而且重载形式多变,可以根据不同的参数生成对应的数据.

package com.itheima.time;
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);
    /*
    初始化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);
 }
}

 

为LocalDateTime添加时区信息(拓展)

       在学习ZonedDateTime的时候,发现了这个对象里面封装的不仅有时间日期,并且还有偏移量+时区,那么时区如何在Java中获取呢,通过提供的一个类ZoneId的getAvailableZoneIds方法可以获取到一个Set集合,集合中封装了600个时区.

//获取所有的时区信息
    Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
    for (String zoneId : availableZoneIds) {
      System.out.println(zoneId);
   }

     同样也提供了获取当前系统默认的时区的方式systemDefault()方法.

//获取当前系统默认的时区信息
    ZoneId zoneId = ZoneId.systemDefault();
    System.out.println(zoneId);

      我们可以通过给LocalDateTime添加时区信息来查看到不同时区的时间,比如说LocalDateTime中当前封装的是上海时间,那么想知道在此时此刻,纽约的时间是什么,就可以将纽约的时区Id添加进去,就可以查看到了,方式如下.

  1. 封装时间LocalDateTime并添加时区信息.
  2. 更改时区信息查看对应时间.
package com.itheima.time;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
/**
* 为LocalDateTime添加时区信息.
*/
public class Java8TimeClassMethodDemo5 {
  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);
 }
}

 

Month枚举类的使用 

       java.time包中引入了Month的枚举,Month中包含标准日历中的12个月份的常量(从JANURAY到DECEMEBER)也提供了一些方便的方法供我们使用.推荐在初始化LocalDate和LocalDateTime对象的时候,月份的参数使用枚举的方式传入,这样更简单易懂而且不易出错,因为如果是老的思维,Calendar传入0的话,那么会出现异常.

/**
* 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方法在LocalDate与LocalTime中的使用     

       想要修改某个日期/时间对象的现有实例时,我们可以使用plus和minus方法来完成操作.Java8中日期时间相关的API中的所有实例都是不可改变的,一旦创建LocalDate,LocalTime,LocalDateTime就无法修改他们(类似于String),这对于线程安全非常有利.

 

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) 增加小时

 

package com.itheima.time.plus;
import org.junit.Test;
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);
    //计算当前时间的周后的时间.
    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);
 }
}

package com.itheima.time.plus;
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);
 }
}

     本文中都是使用plusXXX的方法进行演示,实际上也有对应的减少方法,以minus开头的方法对应的即为减少,实际上minus方法调用的也是plus方法,只不过传入的参数是负数.

plus和minus方法的应用

     刚才学习到的plusXXX相关的方法都是添加了数值到具体的某一项上,根据观察还有两个单独的plus方法,接下来我们来学习这两个单独的plus方法.

 

plus(TemporaAmount amountToAdd)

      TemporaAmount是一个接口,当接口作为方法的参数的时候,实际上传入的是接口的实现类对象,根据查看这个接口的体系,可以看到这个接口有一个实现类,名字叫做Period,在学习第一节的时候,说明了这个类表示一段时间.

      如何使用Period来表示一段时间呢?这个类本身提供了of(int year,int month,intday)来表示,例: Period.of(1,2,3)返回的对象表示的即为1年2个月3天这么一个时间段,我们可以使用plus方法添加这个时间段.

package com.itheima.time.plus;
import java.time.LocalDate;
import java.time.Period;
/*
今天程序员小郝在查看自己的车辆保险记录的时候查看到还有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 + ".");
 }
}

 

plus(long l,TemporaUnit unit)

      在实际开发过程中,可能还会更精准的去操作日期或者说增加一些特殊的时间,比如说1个世纪,1个半天,1千年,10年等,Java8提供了这些日期的表示方式而不需要去单独进行计算了.TemporaUnit是一个接口,通过查看体系接口发现,可以使用子类ChronoUnit来表示,ChronoUnit封装了很多时间段供我们使用.

package com.itheima.time.plus;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Period;
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);
 }
}

注意第一个参数为单位,第二个参数为时间长度.
例:  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) 修改年
package com.itheima.time.with;
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);
 }
}

with(TemporalField field, long newValue)

       TemporalField是一个接口,通过查看体系结构,可以使用它的子类ChronoField,ChronoField中封装了一些日期时间中的组成部分,可以直接选择之后传入第二个参数进行修改.
例:with(ChronoField.DAY_OF_MONTH,1); 将日期中的月份中的天数改为1.
例:with(ChronoField.YEAR,2021); 将日期中的年份改为2021.

package com.itheima.time.with;
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,1);
    System.out.println("修改前错误的时间是:" + time);
    System.out.println("修改完成之后的时间是:" + endTime);
 }
}

 

调节器TemporalAdjuster与查询TemporalQuery

   

TemporalAdjuster

      在上一节学习的with方法中学习了可以通过with方法修改日期时间对象中封装的数据,但是有一些时候可能会做一些复杂的操作,比如说将时间调整到下个周的周日,下一个工作日,或者本月中的某一天,这个时候可以使用调节器TemporalAdjuster来更方便的处理日期.

     with方法有一个重载形式,需要传入一个TemporalAdjuster对象,通过查看发现TemporalAdjuster是一个接口,方法的参数是一个接口,那么实际上传入的是这个接口的实现类对象.

 在以上的描述中,发现了一个叫做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是一个函数式接口,里面有一个抽象方法叫做TemporaladjustInto(Temporal temporal);传入一个Temporal对象通过实现逻辑返回一个Temporal对象,Temporal是LocalDate,LocalTime相关日期类的父接口,实际上传入的就是一个时间日期对象返回一个时间日期对象.

package com.itheima.time;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.TemporalAdjuster;
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);
 }
}

 

DayOfWeek的使用

      DayOfWeek是一周中星期几的枚举类,其中封装了从周一到周日.

package com.itheima.time;
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);
 }
}

 

自定义TemporalAdjuster调节器


 通过Java8本身提供的TemporalAdjusters中的方法可以完成一些常用的操作,如果要自定义日期时间的更改逻辑,可以通过实现TemporalAdjuster类接口中的方式来完成.

  • 创建类实现TemporalAdjuster接口
  • 实现TemporalAdjuster中的adjustInto方法,传入一个日期时间对象,完成逻辑之后返回日期时间对象.
  • 通过with方法传入自定义调节器对象完成更改.

例:假如员工一个月中领取工资,发薪日是每个月的15号,如果发薪日是周末,则调整为周五.

package com.itheima.time.impl;
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;
}

}
package com.itheima.time;
import com.itheima.time.impl.PayDayAdjuster;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
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);
 }
}

 

TemporalQuery的应用

      学习的时态类对象(LocalDate,LocalTime)都有一个方法叫做query,可以针对日期进行查询.R query(TemporalQuery query)这个方法是一个泛型方法,返回的数据就是传入的泛型类的类型,TemporalQuery是一个泛型接口,里面有一个抽象方法是 R queryFrom(TemporalAccessor temporal),TemporalAccessor是Temporal的父接口,实际上也就是LocalDate,LocalDateTime相关类的顶级父接口,这个queryFrom的方法的实现逻辑就是,传入一个日期/时间对象通过自定义逻辑返回数据.

如果要计划日期距离某一个特定天数差距多少天,可以自定义类实现TemporalQuery接口并且作为参数传入到query方法中.
例:计算当前时间距离下一个劳动节还有多少天?

package com.itheima.time.impl;
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;
 }
}



package com.itheima.time;
import com.itheima.time.impl.UntilDayQueryImpl;
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的转换

      Java8中的java.time包中并没有提供太多的内置方式来转换java.util包中用预处理标准日期和时间的类,我们可以使用Instant类作为中介,也可以使用java.sql.Date和java.sql.Timestamp类提供的方法进行转换.

 

java.util.Date转换为java.time.LocalDate

      java.time包中并没有提供很多的方式来进行直接转换,但是给之前的Date类,Calendar类在Java1.8都提供了一个新的方法,叫做toInstant,可以将当前对象转换为Instant对象,通过给Instant添加时区信息之后就可以转换为LocalDate对象.

package com.itheima.time.convert;
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);
 }
}

java.sql.Date类中的转换方法使用

     java.sql.Date类中提供直接转换为LocalDate的方法,toLocalDate

package com.itheima.time.convert;
import java.time.*;
import java.sql.Date;
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.Timestamp类中的转换方法使用

      TimeStamp是时间戳对象,通过传入一个毫秒值对象进行初始化.

package com.itheima.time.convert;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
public class Java8TimeDateToLocalDateDemo3 {
  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);
 }
}

 

将java.util包中的类转换为java.time包中的相应类

    通过编写转换工具类达到传入Date对象直接进行转换的转换或者将新的日期时间类转换为Date对象

package com.itheima.time.convert;
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方法获取到.
package com.itheima.time.convert;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
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.Calendar类转换为java.time.ZonedDateTime类

        Calendar对象自Java1.1开始提供了一个方法获取时区对象的方法,getTimeZone,要将Calendar对象转换为ZonedDateTime需要先获取到时区对象.从Java1.8开始TimeZone类提供了一个方法可以获取到ZonedId.获取到zoneId之后就可以初始化ZonedDateTime对象了,ZonedDateTime类有一个ofInstant方法,可以将一个Instant对象和ZonedId对象作为参数传入构造一个ZonedDateTime对象.

package com.itheima.time.convert;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class Java8TimeCalendarToLocalDateDemo1 {
  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);
 }
}

 

将java.util.Calendar类转换为java.time.LocalDateTime类

      Calendar对象可以获取到年月日时分秒的信息,这些信息可以作为LocalDateTime构造方法的参数.

package com.itheima.time.convert;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.TimeZone;
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 + 1, day,hour, minute, second);
    System.out.println("转换前的Calendar对象是:" + cal);
    System.out.println("转换后的LocalDateTime对象是:" + localDateTime);
 }
}

注意:Calendar获取到的月份依旧是从0开始的,需要在原有的基础上+1,如果不加1,轻则月份少算了1个月,重则出现异常


 

 

日期的解析与格式化DateTimeFormatter

  • SimpleDateFormat类在刚开始的讲过了是线程不安全的,所以Java8提供了新的格式化类 DateTimeFormatter.
  • DateTimeFormatter类提供了大量预定义格式化器,包括常量(如ISO_LOCAL_DATE),模式字母(如yyyy-MM-dd)以及本地化样式.
  • 与SimpleDateFormat不同的是,新版本的日期/时间API的格式化与解析不需要在创建转换器对象再进行转换了,通过时间日期对象的parse/format方法可以直接进行转换.

LocalDate类定义的parse和format方法

      format方法需要传入一个DateTimeFormatter对象,实际上查看DateTimeFormatter类之后,指定DateTimeFormatter中的常量即可指定格式化方法,常用的格式化方式有ISO_DATE_TIME/ISO_DATE.

package com.itheima.time;
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);
 }
}

 

对日期进行格式化 

      通过DateTimeFormatter的ofLocalizedDate的方法也可以调整格式化的方式.

此方法需要传入一个FormatStyle类对象,查看后发现FormatStyle对象是一个枚举类,其中有几种方式如下.

  • Full:全显示(年月日+星期)
  • Long:全显示(年月日)
  • Medium:缩略显示(没有年月日汉字)
  • SHORT:精简显示(精简年+月日)
package com.itheima.time;
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);
 }
}

注意:此种方式在不同时区的显示方式不一样,在其他时区不会显示中文,会根据当前系统的默认时区来进行区别显示.

 

自定义格式化格式   

     除了系统的自带的方式之外,也可以通过DateTimeFormatter类提供的ofPattern方式创建自定时格式化器,格式化的写法与之前使用的SimpleDateFormat相同.

package com.itheima.time;
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);
 }
}

 



配套视频: https://www.bilibili.com/video/BV1aZ4y1j78G?p=1

配套文档:https://download.csdn.net/download/Dug_Zhang/12754601

 

 

百度云:

完整视频:http://yun.itheima.com/course/658.html?2008zzp
配套资料:https://pan.baidu.com/s/1p0AGD3Vw0P6ac90FjmxbpA 提取码:ed1l

 

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值