Java8新日期API精华篇

为什么要学习新的日期API

  1. 旧的日期API(Date/Calender)计算困难,毫秒值计算容易出现误差

  2. 旧的日期API(Date/Calender)线程不安全

    SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个

    SimpleDateFormat类中的Calender对象有可能会出现异常

  3. 旧的日期API(Date/Calender)不符合开发规范,使用了魔法数字,初始化月份需-1等操作

​ 最终JavaSE 8中引入了java.time包,这种全新的包从根本上解决了长久以来的存在的诸多弊端,java.time包基于Joda-Time库构件,是一种免费的开源解决方案。

java.time包

常用类的概述和功能介绍

Instant类

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

Duration类

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

Period类

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

LocalDate类

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

LocalTime类

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

LocalDateTime类

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

ZonedDateTime类

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

now()

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

​ now():根据当前日期或时间创建实例,还可以通过Year、YearMonth等类获取更精准的某些信息。

public static void  nowDemoMethod(){

        // 使用now()方法创建 Instnat的实例对象
        Instant instantNow = Instant.now();
        System.out.println("Instant:"+instantNow);
        // 使用now()方法创建 LocalDate的实例对象
        LocalDate localDateNow = LocalDate.now();
        System.out.println("LocalDate:"+localDateNow);
        // 使用now()方法创建 LocalTime的实例对象
        LocalTime localTimeNow = LocalTime.now();
        System.out.println("LocalTime:"+localTimeNow);
        // 使用now()方法创建 LocalDateTime的实例对象
        LocalDateTime localDateTimeNow = LocalDateTime.now();
        System.out.println("LocalDateTime:"+localDateTimeNow);
        // 使用now()方法创建 ZonedDateTime的实例对象
        ZonedDateTime zonedDateTimeNow = ZonedDateTime.now();
        System.out.println("ZonedDateTime:"+zonedDateTimeNow);
        System.out.println();
         // 使用now()方法创建 Year的实例对象
         Year yearNow = Year.now();
         System.out.println("Year:"+yearNow);
         // 使用now()方法创建 YearMonth的实例对象
         YearMonth yearMonthNow = YearMonth.now();
         System.out.println("YearMonth:"+yearMonthNow);
         // 使用now()方法创建 MonthDay的实例对象
         MonthDay monthDayNow = MonthDay.now();
         System.out.println("MonthDay:"+monthDayNow);
    }

of()

​ 指定任意时间点。

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

public static void ofDemoMethod(){

        // 初始化2020年10月31日的 LocalDate对象
        LocalDate localDate=LocalDate.of(2020, Month.OCTOBER, 31);
        System.out.println("LocalDate:"+localDate);

        /*
        * 初始化晚上8点0分0秒的 LocalTime对象 -> 如果是晚上时间,需要加12。
        * 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) -> 根据小时/分钟/毫秒/纳秒生成对象
        * */
        LocalTime localTime = LocalTime.of(20, 0);
        // LocalTime:20:00  若末尾秒为0则省略
        System.out.println("LocalTime:" + localTime);

        /*
        * 初始化2020年10月31日下午8点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 localDateTime = LocalDateTime.of(2020, 10, 31, 20,0);
        // LocalDateTime2020-10-31T20:00
        System.out.println("LocalDateTime:" + localDateTime);

        /*
        * LocalDateTime的 of方法的特殊使用:
        * LocalDateTime.of(LocalDate date, LocalTime time)
        * */
        LocalDateTime localDateTime1 = LocalDateTime.of(localDate, localTime);
        // LocalDateTime1:2020-10-31T20:00
        System.out.println("LocalDateTime1:" + localDateTime1);
        
    }

时区信息的获取(拓展)

​ ZonedDateTime对象不仅有时间日期,并且还有偏移量+时区,在Java中通过java.time.ZoneId类的**getAvailableZoneIds()方法即可获取到一个Set集合,包含有所有的时区信息,大约有590个。也可以通过systemDefault()查看当前系统默认时区,java.time.LocalDateTime类的atZone()获取时区时间信息,java.time.ZonedDateTime类的withZoneSameInstant()**查看不同时区的时间。

public static void zoneIdDemoMethod(){

        // 获取所有时区信息
        Set<String> allZoneList=ZoneId.getAvailableZoneIds();
        for (String zoneId : allZoneList) {
            System.out.println(zoneId);
        }
        System.out.println("时区个数为:"+allZoneList.size()+"个");

        // 获取当前系统默认时区
        ZoneId zoneId=ZoneId.systemDefault();
        System.out.println("当前系统默认时区:"+zoneId);

        // 查看不同时区的时间信息
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.OCTOBER,30,22,30,50);
        ZonedDateTime zonedDateTime=localDateTime.atZone(zoneId.of("Asia/Shanghai"));
        System.out.println("上海当前时间是:"+zonedDateTime);
        // 更改时区,查看东京当前时间
        ZonedDateTime djzonedDateTime=zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
        System.out.println("同时刻东京时间为:"+djzonedDateTime);
    }

Month枚举类

​ Month枚举类中包含标准日历的12个月份常量,也提供了一些方便的方法供我们使用。

JANUARY-1;FEBRUARY-2;MARCH-3; APRIL-4;MAY-5;JUNE-6;JULY-7;AUGUST-8;SEPTEMBER-9;OCTOBER-10;NOVEMBER-11;DECEMBER-12;

public static void monthDemoMethod(){

        // 初始化 LocalDateTime等时间月份推荐使用枚举类Month 简单明了
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.OCTOBER,30,22,30,50);
        System.out.println(localDateTime);
        // Month枚举类of()可根据传入的数字返回对应的月份枚举
        Month month=Month.of(11);
        System.out.println(month);
        // Month枚举类所有的枚举值
        System.out.println(Arrays.toString(Month.values()));

    }

plus()/minus()

修改某个日期/时间对象的现有实例时,可以使用plus和minus相关方法来完成操作。

​ Java8中日期时间相关API中的所有实例都是不可改变的,一旦创建LocalDate、LocalTime、LocalDateTime就无法修改它们(类似String),这对于线程安全是非常有利的。

plusXXX()/minusXXX()

​ plusXXX()与minusXXX是一组对应方法,前者是增加具体值,后者是减少具体值,实际上minus()调用的也是plus(),只不过传入的参数是负数。

plus()与minus()方法在LocalDate与LocalTime中的使用

  • LocalDate

    LocalDate plusDays(long days) 增加天数

    LocalDate minusDays(long days) 减少天数

    LocalDate plusWeeks(long weeks) 增加周数

    LocalDate minusWeeks(long weeks) 减少周数

    LocalDate plusMonths(long months) 增加月数

    LocalDate minusMonths(long months) 减少月数

    LocalDate plusYears(long years) 增加年数

    LocalDate minusYears(long years) 减少年数

  • LocalTime

    LocalTime plusNanos(long nanos) 增加纳秒

    LocalTime minusNanos(long nanos) 减少纳秒

    LocalTime plusSeconds(long seconds) 增加秒

    LocalTime minusSeconds(long seconds) 减少秒

    LocalTime plusMinutes(long minutes) 增加分钟

    LocalTime minusMinutes(long minutes) 减少分钟

    LocalTime plusHours(long hours) 增加小时

    LocalTime minusHours(long hours) 减少小时

public static void plusXXXAndminusXXXDemoMethod(){
        /**
         * LocalDate
         */
        // 初始化日期为2020年11月1日
        LocalDate localDate=LocalDate.of(2020, Month.NOVEMBER, 1);
        System.out.println("当前日期为:"+localDate);
        System.out.println();
        // 四天后的日期是 2020-11-05
        LocalDate localDate1=localDate.plusDays(4);
        System.out.println("四天后的日期为:"+localDate1);
        System.out.println("原日期为:"+localDate1.minusDays(4));
        // 三周后的日期是 2020-11-22
        LocalDate localDate2=localDate.plusWeeks(3);
        System.out.println("三周后的日期为:"+localDate2);
        System.out.println("原日期为:"+localDate2.minusWeeks(3));
        // 二月后的日期是 2021-01-01
        LocalDate localDate3=localDate.plusMonths(2);
        System.out.println("二月后的日期为:"+localDate3);
        System.out.println("原日期为:"+localDate3.minusMonths(2));
        // 一年后的日期是 2021-11-01
        LocalDate localDate4=localDate.plusYears(1);
        System.out.println("一年后的日期为:"+localDate4);
        System.out.println("原日期为:"+localDate4.minusYears(1));

        /**
         * LocalTime
         */
        System.out.println("******LocalTime******");
        // 初始化时间为22点43分56秒216纳秒
        LocalTime localTime=LocalTime.of(22,43,56,216);
        System.out.println("当前时间为:"+localTime);
        System.out.println();
        // 200纳秒后的时间是 22:43:56.000000416
        LocalTime localTime1=localTime.plusNanos(200);
        System.out.println("200纳秒后的时间为:"+localTime1);
        System.out.println("原时间为:"+localTime1.minusNanos(200));
        // 30秒后的时间是 22:44:26.000000216
        LocalTime localTime2=localTime.plusSeconds(30);
        System.out.println("30秒后的时间为:"+localTime2);
        System.err.println("原时间为:"+localTime2.minusSeconds(30));
        // 20分钟后的时间是 23:03:56.000000216
        LocalTime localTime3=localTime.plusMinutes(20);
        System.out.println("20分钟后的时间为:"+localTime3);
        System.out.println("原时间为:"+localTime3.minusMinutes(20));
        // 10小时后的时间是 08:43:56.000000216
        LocalTime localTime4=localTime.plusHours(10);
        System.out.println("10小时后的时间为:"+localTime4);
        System.out.println("原时间为:"+localTime4.minusHours(10));

    }

plus()/minus()

​ 下面为大家介绍单独使用plus()与minus()

  • plus/minus()

    plus/minus(TemporalAmount amountToAdd) 为当前日期/时间添加一段时间

    ​ TemporalAmount是一个接口,当接口作为方法的参数时,实际传入的是接口的实现类对象。该接口体系中比较常用的实现类为Period,该类表示一段时间。Period类的of(int year,int month,int day)可用于表示一段时间,例如Period.of(1,2,3)返回的对象即为一年二个月三天这么一个时间段。

    plus/minus(long amountToAdd, TemporalUnit unit) 为当前日期/时间添加一些特殊事件,如世纪、年、半天…

    ​ 在实际开发过程中,可能还会更精准的去操作日期或者说增加一些特殊的时间,比如说1个世纪、1个半天、1千年、10年等,Java8提供了这些日期的表示方式而不需要去单独进行计算了。

    ​ TemporalUnit是一个接口,该接口体系中常用的实现类是ChronoUnit,该类封装了很多时间段供我们使用:NANOS-纳秒、MICROS-微秒、MILLIS-毫秒、SECONDS-秒、MINUTES-分钟、HOURS-小时、HALF_DAYS-半天、DAYS-天、WEEKS-星期、MONTHS-月、YEARS-年、DECADES-十年、CENTURIES-世纪、MILLENNIA-千年、ERAS-十亿年、FOREVER-永远。

public static void plusAndminusDemoMethod(){

        LocalDateTime now=LocalDateTime.of(2020, Month.NOVEMBER,1,22,30,30);
        // 在当前日期基础上增加2年1个月4天  
        LocalDateTime endTime=now.plusYears(2).plusMonths(1).plusDays(4);
        System.out.println("当前时间:"+now+",增加后的时间:"+endTime);
        
        // plus(TemporalAmount amountToAdd)
        Period period=Period.of(2, 1, 4);
        LocalDateTime endTime1=now.plus(period);
        LocalDateTime startTime1=endTime1.minus(period);
        System.out.println("当前时间:"+startTime1+",增加后的时间:"+endTime1);
        
        // plus(long amountToAdd,  TemporalUnit unit)
        // 在当前日期基础上增加10年
        LocalDateTime endTime2=now.plus(1, ChronoUnit.DECADES);
        LocalDateTime startTime2=endTime2.minus(1, ChronoUnit.DECADES);
        System.out.println("当前时间:"+startTime2+",十年后的时间:"+endTime2);
        // 在当前日期基础上增加半天
        LocalDateTime endTime3=now.plus(1, ChronoUnit.HALF_DAYS);
        LocalDateTime startTime3=endTime3.minus(1, ChronoUnit.HALF_DAYS);
        System.out.println("当前时间:"+startTime3+",半天后的时间:"+endTime3);

    }

with()

​ 当想要直接修改日期时,可以使用with(),该方法提供了很多种修改时间的方式

  1. 直接使用对应方法修改日期时间中的某个值

    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) 修改年

  2. with(TemporalField field, long newValue) 指定修改日期时间中的某个值

    ​ TemporalField是一个接口,该接口体系中常用的实现类是ChronoField,该类封装了一些日期时间中的组成成分,可以直接选择后传入第二个参数进行修改。

    ​ 比如:with(ChronoField.MONTH_OF_YEAR,10) 将日期时间中的月份修改为10月

    ​ 比如:with(ChronoField.YEAR,2021) 将日期时间中的年修改为2021年

    ​ TemporalField类中的常用日期时间的表示字段有:YEAR-年、MONTH_OF_YEAR-月、DAY_OF_MONTH-日、HOUR_OF_DAY-时、MINUTE_OF_HOUR-分、SECOND_OF_MINUTE-秒。

public static void withDemoMethod(){

        // 初始化日期时间
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.SEPTEMBER,27,21,15,55);
        // 修改时间日为28日
        LocalDateTime changeTime=localDateTime.withDayOfMonth(28);
        System.out.println("原时间:"+localDateTime+",修改后的时间:"+changeTime);
        // with(TemporalField field,long newValue) 
        // 修改时间月为10月
        LocalDateTime changeTime2=changeTime.with(ChronoField.MONTH_OF_YEAR,10);
        System.out.println("原时间:"+localDateTime+",两次修改后的时间:"+changeTime2);
        
    }

TemporalAdjuster调节器

​ with(TemporalAdjuster adjuster) 该方法可用于更复杂的日期时间操作,比如将时间调整到本月第一天、下月第一天、本年第一天等,这时可以使用调节器TemporalAdjuster来更方便的处理。该方法需要传入一个TemporalAdjuster对象,TemporalAdjuster是一个接口,实际传入的是TemporalAdjuster接口的实现类对象。

TemporalAdjusters类的一些静态方法可以返回TemporalAdjuster对象。TemporalAdjusters常用的静态方法有:

​ static TemporalAdjuster firstDayOfMonth() 本月的第一天

​ static TemporalAdjuster lastDayOfMonth() 本月最后一天

​ static TemporalAdjuster firstDayOfYear() 本年的第一天

​ static TemporalAdjuster lastDayOfYear() 本年最后一天

​ static TemporalAdjuster firstDayOfNextMonth() 下个月的第一天

​ static TemporalAdjuster firstDayOfNextYear() 下一年第一天

public static void temporalAdjusterDemo(){
        // 初始化当前日期对象
        LocalDate localDate=LocalDate.of(2020, 11, 8);
        // with(TemporalAdjuster adjuster)
        LocalDate firstDayOfMonth=localDate.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("当月第一天:"+firstDayOfMonth);

        LocalDate lastDayOfMonth=localDate.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("当月最后一天:"+lastDayOfMonth);

        LocalDate firstDayOfYear=localDate.with(TemporalAdjusters.firstDayOfYear());
        System.out.println("本年第一天:"+firstDayOfYear);

        LocalDate lastDayOfYear=localDate.with(TemporalAdjusters.lastDayOfYear());
        System.out.println("本年最后一天"+lastDayOfYear);

        LocalDate firstDayOfNextMonth=localDate.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("下个月第一天:"+firstDayOfNextMonth);

        LocalDate firstDayOfNextYear=localDate.with(TemporalAdjusters.firstDayOfNextYear());
        System.out.println("下一年第一天:"+firstDayOfNextYear);
    }

注意:TemporalAdjuster是一个接口,with(TemporalAdjuster adjuster) 实际上传入的是TemporalAdjuster接口的实现类对象。TemporalAdjusters并不是TemporalAdjuster的实现类,只不过TemporalAdjusters的静态方法返回了TemporalAdjuster对象。

DayOfWeek

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

​ MONDAY-1、TUESDAY-2、WEDNESDAY-3、THURSDAY-4、FRIDAY-5、SATURDAY-6、SUNDAY-7

public static void dayOfWeekDemo(){

        // 初始化当前日期对象
        LocalDate localDate=LocalDate.of(2020, 11, 10);
        // 将当前日期修改为下周日
        LocalDate s=localDate.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println("下周日是:"+s);
        // 将当前日期修改为上周三
        LocalDate w=localDate.with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));
        System.out.println("上周三是:"+w);

    }

自定义TemporalAdjuster调节器

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

对日期自定义修改大体步骤可分三步

  1. 创建类实现TemporalAdjuster接口。
  2. 在创建的类中实现TemporalAdjuster接口的Temporal adjustInto(Temporal temporal)方法,传入一个日期时间对象,完成自定义的处理逻辑之后返回修改后的日期时间对象。
  3. 通过with()方法传入自定义调节器对象完成日期时间修改。

​ 实现需求:公司员工月中领取工资,发薪日是每个月的15日,如果发薪日是周末(周六、周日),则调整为周五。

实现TemporalAdjuster接口的PayDayAdjuster类

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

class PayDayAdjuster implements TemporalAdjuster {


    @Override
    public Temporal adjustInto(Temporal temporal) {
        
        // Temporal类是java.time包下所有日期时间类对象的顶级父接口,实际上可以理解传入的是LocalDate或者LocalTime对象
        // 1.将Temporal对象转换为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.SATURDAY||realPayDay.getDayOfWeek()==DayOfWeek.SUNDAY){
            realPayDay=realPayDay.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        }
        return realPayDay;
    }

}

测试方法

public static void myDefinedTemporalAdjusterDemo(){
        // 预计发薪日 2020-11-15
        LocalDate payDay=LocalDate.of(2020, Month.NOVEMBER, 15);
        System.out.println("预计发薪日"+payDay);
        // 实际发薪日 2020-11-13
        LocalDate realPayDay=payDay.with(new PayDayAdjuster());
        System.out.println("实际发薪日"+realPayDay);

    }

TemporalQuqry

​ 时态类(LocalDate、LocalTime、LocalDateTime)对象都有query)()方法,可以针对日期进行查询,public <R> R query(TemporalQuery<R> query) 此方法是一个泛型方法,返回数据的类型就是传入实现TemporalQuery泛型接口的类所指定的类型。如果要查询指定日期距离某一天有多少天,可以自定义类实现TemporalQuery<R>泛型接口并作为参数传到query()方法中,进行查询。

对日期自定义的查询大体步骤也可分三步:

  1. 创建类实现TemporalQuery<R>泛型接口。
  2. 在创建的类中实现TemporalQuery<R>泛型接口的R queryFrom(TemporalAccessor temporal)方法,传入一个日期时间对象,完成自定义的处理逻辑之后返回查询到的信息。
  3. 通过query()方法传入自定义实现TemporalQuery<R>泛型接口的对象完成日期信息查询。

Test-1

​ 实现需求:计算当前时间距离下一个劳动节有多少天。

实现TemporalQuery泛型接口的UtilDayQueryImpl类

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;

class UtilDayQueryImpl implements TemporalQuery<Long> {

    @Override
    public Long queryFrom(TemporalAccessor temporal) {

        // TemporalAccessor类是Temporal类的父类,同样是java.time包下所有日期时间类对象的顶级父接口,实际上可以理解传入的是LocalDate或者LocalTime对象
        // 1.将TemporalAccessor对象转换为LocalDate对象
        LocalDate now= LocalDate.from(temporal);
        // 2.封装本年的劳动节日期
        LocalDate laborDay=LocalDate.of(now.getYear(), Month.MAY, 1);
        // 3.判断当前日期是否超过本年劳动节
        if(now.isAfter(laborDay)){
            laborDay=laborDay.plusYears(1);
        }
        // 4.通过ChronoUnit的between()来计算两个日期之间相差的天数
        Long days=ChronoUnit.DAYS.between(now, laborDay);

        return days;
        
    }

}

测试方法

public static void temporalQueryDemo(){

        LocalDate now = LocalDate.of(2020, Month.NOVEMBER, 14);
        Long days=now.query(new UtilDayQueryImpl());
        System.out.println(now+" 距离下一个劳动节还有:"+days+"天");

    }

Test-2

实现需求:计算当前时间距离下一个劳动节/儿童节/圣诞节各相差多少天

实现TemporalQuery泛型接口的TestQueryImpl类

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;

class TestQueryImpl implements TemporalQuery<Long[]> {

    @Override
    public Long[] queryFrom(TemporalAccessor temporal) {

        // TemporalAccessor类是Temporal类的父类,同样是java.time包下所有日期时间类对象的顶级父接口,实际上可以理解传入的是LocalDate或者LocalTime对象
        // 1.将TemporalAccessor对象转换为LocalDate对象
        LocalDate date= LocalDate.from(temporal);
        // 2.封装本年的劳动节/儿童节/圣诞节日期
        LocalDate d1=LocalDate.of(date.getYear(), Month.MAY, 1);
        LocalDate d2=LocalDate.of(date.getYear(), Month.JUNE, 1);
        LocalDate d3=LocalDate.of(date.getYear(), Month.DECEMBER, 25);
        // 3.判断当前日期是否超过本年的劳动节/儿童节/圣诞节
        if(date.isAfter(d1)){
            d1=d1.plusYears(1);
        }
        if(date.isAfter(d2)){
            d2=d2.plusYears(1);
        }
        if(date.isAfter(d1)){
            d3=d3.plusYears(3);
        }
        // 4.通过ChronoUnit的between()来计算两个日期之间相差的天数
        Long[] threeDays={ChronoUnit.DAYS.between(date, d1),ChronoUnit.DAYS.between(date, d2),ChronoUnit.DAYS.between(date, d3)};

        return threeDays;
        
    }

}

测试方法

public static void test2TemporalQueryDemo(){

        LocalDate now = LocalDate.of(2020, Month.NOVEMBER, 14);
        Long[] threeDays=now.query(new TestQueryImpl());
        System.out.println(now+" 距离下一个劳动节还有:"+threeDays[0]+"天");
        System.out.println(now+" 距离下一个儿童节还有:"+threeDays[1]+"天");
        System.out.println(now+" 距离下一个圣诞节还有:"+threeDays[2]+"天");

    }

将java.sql.Date/Timestamp转换为java.time.LocalDate/LocalDateTime

​ java.sql.Date类中提供直接转换为java.time.LocalDate的**toLocalDate()**方法

​ java.sql.Timestamp类是时间戳对象,可以通过传入一个毫秒值进行初始化,可以通过**toLocalDateTime()**方法直接转换为java.time.LocalDateTime类的对象。

public static void sqlDateToLocalDate(){
        // 初始化java.sql.Date对象
        java.sql.Date sqlDate=new java.sql.Date(System.currentTimeMillis());
        LocalDate localDate=sqlDate.toLocalDate();
        System.out.println("转换前的java.sql.Date对象是:"+sqlDate);
        System.out.println("转换后的java.time.LocalDate对象是:"+localDate);

        // 初始化java.sql.Timestamp对象 时间戳对象
        Timestamp timestamp=new Timestamp(System.currentTimeMillis());
        LocalDateTime localDateTime=timestamp.toLocalDateTime();
        System.out.println("转换前的java.sql.Timestamp对象是:"+timestamp);
        System.out.println("转换后的java.time.LocalDateTime对象是:"+localDateTime);

    }

将java.util.Date转换为java.timeLocalDate

​ 在老项目中,可能需要将Date或者Calendar转换为java.time包中的LocalDate、LocalDateTime等类。

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

使用Instant类将java.util.Date转换为java.time.LocalDate

​ java.time包中并没有提供很多的方式来进行直接转换,但是给之前的Date、Calender类在Java8版本中提供了toInstant()的新方法。

转换具体步骤分为三步:

  1. 将Date对象转换为Instant对象
  2. 将Instant对象转换为ZonedDateTime对象
  3. 通过ZonedDateTime的toLocalDate()方法转换为LocalDate对象
public static void dateToLocalDate(){
        // 初始化Date对象
        Date date =new Date();
        // 1.将Date对象转换为Instant对象
        Instant instant=date.toInstant();
        // 2.将Instant对象转换为ZonedDateTime对象
        // Date类包含日期和时间信息,但不提供时区信息,和Instant一样,通过Instant的atZone()来添加时区信息
        ZonedDateTime zonedDateTime=instant.atZone(ZoneId.systemDefault());
        // 3.通过ZonedDateTime的toLocalDate()方法完成转换
        LocalDate localDate=zonedDateTime.toLocalDate();

        System.out.println("Date对象是:"+date);
        System.out.println("转换后的LocalDate对象是:"+localDate);
    }

使用java.sql.Date将java.util.Date转换为java.time.LocalDate

转换具体步骤分为两步:

  1. 将java.util.Date对象转换为java.sql.Date对象
  2. 通过java.sql.Date类的toLocalDate()方法转换为LocalDate等对象
public static void utilDateToLocalDate2(){

        // 初始化java.util.Date对象
        Date utilDate=new Date();

        // 将java.util.Date转换位java.sql.Date
        java.sql.Date sqlDate=new java.sql.Date(utilDate.getTime());
        Timestamp timestamp=new Timestamp(utilDate.getTime());
        LocalDate localDate=sqlDate.toLocalDate();
        LocalDateTime localDateTime=timestamp.toLocalDateTime();
        System.out.println("转换前的java.util.Date对象是:"+utilDate);
        System.out.println("转换前的java.sql.Date对象是:"+sqlDate);
        System.out.println("转换前的java.sql.Timestamp日期时间对象是:"+timestamp);
        System.out.println("转换后的java.time.LocalDate对象是:"+localDate);
        System.out.println("转换后的java.time.LocalDateTime日期时间对象是:"+localDateTime);
    }

Calendar转换为java.time.LocalDate

​ Calendar转换比较麻烦,需要通过ZonedDateTime对象的ofInstant()方法将Calendar相关的Instant对象和ZoneId对象封装为ZonedDateTime对象,再利用toLocalDate()方法得到最终的LocalDate或LocalDateTime对象。

具体步骤为 :

  1. 初始化Calendar对象
  2. 利用Calendar对象的getTimeZone()方法获取时区对象
  3. 利用TimeZone对象的toZoneId()方法获取构建ZonedDateTime的ZoneId对象
  4. 将Instant对象和ZoneId对象封装为ZonedDateTime对象
  5. 转换为LocalDate/LocalDateTime对象
public static void calendarToLocalDate(){

        // 1.初始化Calendar对象
        Calendar calendar=Calendar.getInstance();
        // 2.利用Calendar对象的getTimeZone()方法获取时区对象
        TimeZone timeZone=calendar.getTimeZone();
        // 3.利用TimeZone对象的toZoneId()方法获取构建ZonedDateTime的ZoneId对象
        ZoneId zoneId=timeZone.toZoneId();
        // 4.将Instant对象和ZoneId对象封装为ZonedDateTime对象
        ZonedDateTime zonedDateTime=ZonedDateTime.ofInstant(calendar.toInstant(), zoneId);
        // 5.转换为LocalDate对象
        LocalDate localDate=zonedDateTime.toLocalDate();

        System.out.println("转换之前的Calendar对象:"+calendar);
        System.out.println("转换之后的ZonedDateTime对象:"+zonedDateTime);
        System.out.println("转换之后的LocalDate对象:"+localDate);

    }

Calendar转换为java.time.LocalDateTime

​ Calendar转换简单的方式是:单独获取Calendar对象年月日时分秒部分信息,作为LocalDate、LocalDateTime类of()方法的参数初始化相应的日期时间对象即可。

具体转换分为三步:

  1. 初始化Calendar对象
  2. 通过get()方法获取Calendar对象日期时间部分数据
  3. 将以上获取的参数作为LocalDateTime对象of()方法所需封装的参数进行初始化
public static void calendarToLocalDateTime(){

        // 1.初始化Calendar对象
        Calendar calendar=Calendar.getInstance();
        // 2.通过get()方法获取Calendar对象日期时间部分数据
        int year=calendar.get(Calendar.YEAR);
        int month=calendar.get(Calendar.MONTH);
        int day=calendar.get(Calendar.DAY_OF_MONTH);
        int hour=calendar.get(Calendar.HOUR);
        int minute=calendar.get(Calendar.MINUTE);
        int second=calendar.get(Calendar.SECOND);
        // 3.将以上获取的参数作为LocalDateTime对象of()方法所需封装的参数进行初始化
        // 特别注意:Calendar对象封装的月份是从0开始的,所以month要加1
        LocalDateTime localDateTime=LocalDateTime.of(year,month+1,day,hour,minute,second);

        System.out.println("转换之前的Calendar对象:"+calendar);
        System.out.println("转换之后的LocalDateTime对象:"+localDateTime);

    }

新日期类的格式化DateTimeFormatter

format()/parse()

​ SimpleDateFormat类是线程不安全的,所以Java8提供了新的格式化日期时间类DateTimeFormatter,它是线程安全的。DateTimeFormatter类提供了大量预定义格式化器,包括常量(ISO_DATE_TIME、ISO_DATE、ISO_TIME等),模式字母(如yyyy-MM-dd)以及本地化样式。

​ 与SimpleDateFormat不同,新版的日期/时间API的格式化与解析不需要再创建转换器对象了,通过日期时间对象的format()与parse()方法可以直接进行转换。

public static void timeFormatAndParseDemo(){

        // 初始化LocalDateTime对象
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.NOVEMBER,14,16,25,30);
        // LocalDateTime对象可以直接调用format()方法进行格式化
        String s1=localDateTime.format(DateTimeFormatter.ISO_DATE);
        String s2=localDateTime.format(DateTimeFormatter.ISO_DATE_TIME);

        System.out.println("原时间日期格式"+localDateTime);
        System.out.println("ISO_DATE格式"+s1);
        System.out.println("ISO_DATE_TIME格式"+s2);

        LocalDate parseDate=LocalDate.parse(s1);
        System.out.println("解析后的日期为:"+parseDate);
    }

ofLocalizedDate()

​ 通过DateTimeFormatter的ofLocalizedDate()方法也可以调整格式化的方式。此方法需要传入FormatStyle类对象,FormatStyle是一个枚举类,有以下4种样式

FULL:全显示 年月日+星期,如2020年11月14日 星期六

LONG:全显示 年月日,如2020年11月14日

MEDIUM:缩略显示 没有年月日后缀修饰,如2020-11-14

SHORT:精简显示 精简两位数字年+数字月日,如20-11-14

public static void ofLocalizedDateDemo(){
        
        // 初始化LocalDateTime对象
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.NOVEMBER,14,16,25,30);
        // 通过DateTimeFormatter的ofLocalizedDate()指定解析格式
        String s1=localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
        String s2=localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
        String s3=localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
        String s4=localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));

        System.out.println("FULL全显示:"+s1);
        System.out.println("LONG全显示:"+s2);
        System.out.println("MEDIUM缩略显示:"+s3);
        System.out.println("SHORT精简显示:"+s4);
    }

自定义格式化

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

public static void myDefinedFormatDemo(){

        // 初始化LocalDateTime对象
        LocalDateTime localDateTime=LocalDateTime.of(2020, Month.NOVEMBER,14,16,25,30);
        // 通过DateTimeFormatter的ofPattern()方法自定义格式化样式
        String s=localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String s2=localDateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"));
        String s3=localDateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒"));

        System.out.println("自定义格式化样式:"+s);
        System.out.println("自定义格式化样式二:"+s2);
        System.out.println("自定义格式化样式三:"+s3);
    }

工作忙的时候,抽空写博客太煎熬了呀,完结撒花,哈哈哈~~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值