【Java】基于time包的日期时间

在JDK1.8,Java基于原来的(java.util包)日期时间处理API,进行了重大的升级(java.time包),解决了旧API的种种问题。本文尝试着简单概要地将java.time包中的日期时间进行分类整理,并介绍它的基本用法。

目录

设计思想

方法

time包对比于util包的设计升级

Instant类

Clock时钟

日期时间

时期

时间显示格式

时间格式控制符

解析、格式化时间

时间调整

TemporalAdjuster接口

自定义时间调节器

TemporalQuery接口

util包的时间与time包的时间互转

util包Date转time包LocalDate

Calendar转LocalDateTime

Calendar转ZonedDateTime


背景:

  • 为了解决java.util包中时间日期类的诸多设计不当、多线程不安全的问题
  • 于是引入了java.time包

初识java.time包:

  • JDK1.8引入
  • 基于ISO日历系统,它又是遵循 Gregorian规则(中国公历)

特性:所有类均生成不可变实例(final修饰),它们是线程安全的。并且这些类不提供公共构造函数,也就是说没办法通过new的方式直接创建,需要采用静态工厂方式获取实例。

为什么不应该使用java.util包中的类处理时间:

  • 毫秒值与日期直接转换比较繁琐,其次通过毫秒值来计算时间有大的误差
  • 老版本API线程不安全问题。SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个SimpleDateFormat类中的Calendar对象有可能会出现异常。需要开发者加锁。

Date、Calendar时间处理有误差

Date、Calendar时间处理有误差
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;

/**
 * 程序员小李出生于1995年12月16日,计算在当前这个时间他已经出生了多少天?
 * 算法:
 *  1.利用Date获取当前时间的毫秒值,利用Calendar获取小李出生时间的毫秒值
 *  2.将两个毫秒值做差,再转换成天数
 */
public class Main {

    public static void main(String[] args) {
        Date nowDate = new Date();
        Calendar birthDate = Calendar.getInstance();
        birthDate.set(1995,11,16); //要想设置 12 月份,则需要写11,因为从 0 开始

        long days = (nowDate.getTime() - birthDate.getTime().getTime()) / 1000 / 60 / 60 / 24;
        System.out.println(days); //输出:9152

        // 使用 java8 新版本的API来完成题目要求
        long daysTmp = ChronoUnit.DAYS.between(LocalDate.of(1995, 12, 16), LocalDate.now());
        System.out.println(daysTmp); //输出:9153
    }
}

 

设计思想

通过对java.time包中所有类的观察,我尝试将其中的类进行分类,以便记忆和在不同场景下应用。

日期时间:

  • LocalDate类:
    • 是一个不可变的日期时间对象,表示日期,通常被视为年月日。
    • 例如:年-月-日 yyyy-MM-dd 2020-05-02
  • LocalTime类:
    • 是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度。
    • 例如:时:分:秒 HH:mm:ss 17:01:58.954758600
  • LocalDateTime类:
    • 是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日=时-分-秒。
    • 例如:年-月-日T时:分:秒 2020-05-02T17:01:58.954758600

年月日:

  • Year:获取年份
  • YearMonth:获取年、月
  • MonthDay:获取月、日
  • Month:枚举类。推荐在初始化LocalDate和LocalDateTime对象的时候,月份的参数使用枚举的方式传入,这样更简单易懂而且不易出错
  • DayOfWeek:周的枚举类。

时间间隔:

  • Instant类:
  • 表示当前瞬时时间。
  • 祖鲁时间(格林威治时间/国际标准时间)Instant:2020-05-02T09:01:58.930823200Z
  • Duration类:表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性。
  • Period类:表示一段时间的年、月、日的间隔。

时区:

  • ZonedDateTime类:
    • 带时区的日期时间
    • 是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。
    • 对应ZoneId加LocalDateTime的结合
    • 例如:年-月-日T时:分:秒+08:00[Asia/Shanghai] 表示时区 ZonedDateTime:2020-05-02T17:01:58.955756+08:00[Asia/Shanghai]
  • ZoneId:时区,会根据夏令时调整。
  • TimeZone:
  • ZoneOffest:时区偏移量(比如:+8:00),固定时间的偏移。不会根据夏令时调整,固定不变的
  • OffsetTime:
  • OffsetDateTime:
    • 对应ZoneOffset加LocalDateTime的结合

工具类:

  • DateTimeFormatter:日期时间格式化类
  • ChronoUnit:日期枚举类(在时间加减操作可用到)

方法

以下的方法,几乎time包下的所有可实例化类都能调用。

now()方法:

  • 静态工厂方法;
  • 功能:获取当前时间的实例对象,例如:Instant instantNow = Instant.now(); LocalDate dateNow = LocalDate.now(); Year year = Year.now();

of()方法:

  • 静态工厂方法
  • 功能:设定给定的参数对应的日期/时间对象,基本上每个基本类都有of方法用于生成的对应的对象,而且重载形式对边,可以根据不同的参数生成对应的数据。
  • LocalDate date = LocalDate.of(2018, Month.AUGUST, 8);
  • LocalDateTime timeLocalDateTime.of(2018, Month.AUGUST, 8, 8, 0);

with()方法:修改时间

parse()方法:

  • 静态工厂方法
  • 专注解析时间

plus()方法:

  • 对时间进行加法操作
  • 如果参数是负数也能够有minus方法的效果

minus()方法:对时间进行减法操作

format()方法:对时间的显示格式进行转换

to()方法:把当前时间对象转换成另外一个

at()方法:把这个对象与另一个对象组合起来

 

time包对比于util包的设计升级

time包与util包中的Calendar对比

  • 在java.time当中,星期一的值为1,星期日的值为7,而在Calendar当中星期日的值为1。
  • 在java.time中一月份的值为1,Calendar一月份的值为0(多么反人类的设计)

 

【Java】基于time包的日期时间_第1张图片

  • java.time:包含值对象的基础包
  • java.time.chrono:提供对不同的日历系统的访问
  • java.time.format:格式化和解析时间和日期
  • java.time.temporal:包括底层框架和扩展特性
  • java.time.zone:包含时区支持的类

Instant类

  1. instant:瞬间、立即
  2. java.time.Instant
  3. 可用于时间各种类型之间的转换桥梁

【Java】基于time包的日期时间_第2张图片

import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        Instant instant = Instant.now(); //获取当前瞬时的时间
        System.out.println(instant); //输出:2021-02-10T14:04:54.647769800Z
    }
}

Clock时钟

【Java】基于time包的日期时间_第3张图片

  • Clock是关联上时区的时钟,Clock可以获取时间戳和时区ZoneId,用来代替System.currentTimeMillis()和TimeZone.getDefault()。
  • 它是个抽象类,一共有四个子类(如上图)
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        //系统默认本地时钟:SystemClock
        Clock clock = Clock.systemDefaultZone();
        System.out.println(clock.getZone()); //获取时区信息:Asia/Singapore
        Instant instant = clock.instant();
        System.out.println(instant); //获取日期时间:2021-01-19T01:19:46.595993200Z

        //偏移时钟:OffsetClock
        Clock clock01 = Clock.systemDefaultZone();
        Clock pastClock = clock01.offset(clock01, Duration.ofMillis(-10000));
        System.out.println(pastClock.getZone()); //时区:Asia/Singapore
        System.out.println(clock01.millis() - pastClock.millis());//时间差:当前时间和过去pastClock相差10000毫秒
    }
}

 

日期时间

LocalDate、LocalTime、LocalDateTime:获取所在时区的详细时间

  • LocalDate类:
    • 是一个不可变的日期时间对象,表示日期,通常被视为年月日。
    • 例如:年-月-日 yyyy-MM-dd 2020-05-02
  • LocalTime类:
    • 是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度。
    • 例如:时:分:秒 HH:mm:ss 17:01:58.954758600
  • LocalDateTime类:
    • 是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日=时-分-秒。
    • 例如:年-月-日T时:分:秒 2020-05-02T17:01:58.954758600

实例:为LocalDateTime添加时区信息

import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        // 1、封装 LocalDateTime对象,参数自定义 ->2018年11月11日 8点54分38秒
        LocalDateTime localDateTime = LocalDateTime.of(2018, Month.NOVEMBER, 11, 8, 54, 38);
        // 2、 localDateTime对象现在只是封装了一个时间,并没有时区相关的数据,所以要添加时区信息到对象中,使用 atZone()方法
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
        //上海当前的时间是:2018-11-11T08:54:38+08:00[Asia/Shanghai]
        System.out.println("上海当前的时间是:" + zonedDateTime);

        // 3、更改时区查看其它时区的当前时间,通过 withZoneSameInstant()方法即可
        ZonedDateTime tokyoZonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
        //在上海同一时刻,Asia/Tokyo的时间是:2018-11-11T09:54:38+09:00[Asia/Tokyo]
        System.out.println("在上海同一时刻,Asia/Tokyo的时间是:" + tokyoZonedDateTime);
    }
}

时期

Duration操作的时间跨度是时分秒,外加纳秒

Period操作的时间跨度是年月日

period实例:

import java.time.LocalDateTime;
import java.time.Period;

//需求:今天程序员小张查看自己的车辆保险记录的时候看到还有2年3月8天就到期了,计算到期的时间是什么时候
public class Main {
    public static void main(String[] args) {
        // 1、封装当前时间  -> now方法
        LocalDateTime now = LocalDateTime.now();
        // 2、在当前时间的基础上进行+2年,+3月,+8天的操作
        // 然后获得一个截止日期对象,这个对象表示的时间就是保险到期的时间。
        LocalDateTime endTime = now.plusYears(2).plusMonths(3).plusDays(8);
        //当前的时间是:2020-05-03T13:52:34.316278400保险到期的时间是:2022-08-11T13:52:34.316278400
        System.out.println("当前的时间是:" + now + "保险到期的时间是:" + endTime);

        // plus(TemporalAmount amountToAdd)
        // 1、首先封装 priod.of()方法传进去参数
        Period period = Period.of(2, 3, 8);
        // 2、通过 now()方法的 plus()方法传进去
        LocalDateTime endTime1 = now.plus(period);
        //当前的时间是:2020-05-03T14:06:16.239460900保险到期的时间是:2022-08-11T14:06:16.239460900
        System.out.println("当前的时间是:" + now + "保险到期的时间是:" + endTime1);
    }
}

 

时间显示格式

把时间的显示格式指定为自己想要的格式,功能就如java.util.SimpleDateFormat类一样。

时间格式控制符

  • G 年代标志符
  • y 年
  • M 月
  • d 日
  • h 时 (12小时制)
  • H 时 (24小时制)
  • m 分
  • s 秒
  • S 毫秒
  • E 星期几
  • D 一年中的第几天
  • F 一月中第几个星期(以每个月1号为第一周,8号为第二周为标准计算)
  • w 一年中第几个星期
  • W 一月中第几个星期(不同于F的计算标准,是以星期为标准计算星期数,例如1号是星期三,是当月的第一周,那么5号为星期日就已经是当月的第二周了)
  • a 上午 / 下午 标记符
  • k 时 (24小时制,其值与H的不同点在于,当数值小于10时,前面不会有0)
  • K 时 (12小时值,其值与h的不同点在于,当数值小于10时,前面不会有0)
  • z 时区

解析、格式化时间

直接调用format方法进行时间格式化,直接调用parse方法进行时间的解析。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.now();

        // localDateTime对象可以直接调用format方法进行格式化
        String s1 = localDateTime.format(DateTimeFormatter.ISO_DATE); //ISO_DATE:2020-05-03
        String s2 = localDateTime.format(DateTimeFormatter.ISO_DATE_TIME); //ISO_DATE_TIME:2020-05-03T17:55:04.1945268

        String s3 = "2066-01-06T17:55:04.1945268";
        // 解析字符串的方式通过LocalDateTime类的静态方法parse传入需要解析的字符串即可
        LocalDateTime parse = LocalDateTime.parse(s3); //如果不指定转换格式,则默认为ISO_LOCAL_DATE_TIME(看源码可得)
        System.out.println(parse);
    }
}

通过DateTimeFormatter的ofLocalizedDate的方法也可以调整格式化的方式。其参数需要FormaStyle枚举类。

FormaStyle枚举类:

  • Full:全显示(年月日+星期)
  • Long:全显示(年月日)
  • Medium:缩略显示(没有年月日汉字)
  • SHORT:精简显示(精简年+月日)
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

public class Main {
    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.now();
        // 通过DateTimeFormatter 的 ofLocalizedDate指定解析格式
        String r1 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
        String r2 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
        String r3 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
        String r4 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));

        //FULL:2020年5月3日星期日
        System.out.println("FULL:" + r1);
        //LONG:2020年5月3日
        System.out.println("LONG:" + r2);
        //MEDIUM:2020年5月3日
        System.out.println("MEDIUM:" + r3);
        //SHORT:2020/5/3
        System.out.println("SHORT:" + r4);
    }
}

按规则进行转换

java.time.format.DateTimeFormatter

  • 此类的功能与SimpleDateFormat类的功能类似,此类也是线程安全的;
  • 在写成时间处理工具类时,可作为静态成员变量,而不用每次都new一个SimpleDateFormat实例;
  • 此类是用来创建日期显示的模板,然后对于日期的格式化和解析还是使用LocalDateTime等类的parse静态方法和format方法,其模板属性格式是和SimpleDateFormat一样的
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class Main {
    //时间日期与String互转
    public static void main(String[] args) throws ParseException {
        /*
            时间format
         */
        //java.util包中对时间的处理
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        String dateStr = simpleDateFormat.format(date);
        System.out.println("SimpleDateFormat: " +dateStr);

        //java.time包中对时间的处理
        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy月MM月dd日 HH时mm分ss秒");
        String dateFormatter = formatter.format(localDateTime);
        System.out.println("DateTimeFormatter: " +dateFormatter);

        /*
            时间parse
         */
        String dateStrParse01 = "2066-06-06 16:16:16";
        SimpleDateFormat sdfParse = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //pattern中的'-',':'要和串中的一致
        Date dateParse = sdfParse.parse(dateStrParse01); //向外抛ParseException异常
        System.out.println("SimpleDateFormat Parse: " +dateParse);//+sdfParse.format(dateParse));

        String dateStrParse02 = "2077-07-07 06:06:06";
        DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTimeParse = LocalDateTime.parse(dateStrParse02, parser);
        System.out.println("DateTimeFormatter Parse: " +localDateTimeParse);
    }
}

 

时间调整

在很多时候,我们需要对时间进行调整操作。例如,将当前时间调节到下个周的周日,下一个儿童节,上一个儿童节,我们就需要用到时间调节器。

有关时间调节器的API,都在java.time.temporal包中。

TemporalAdjuster接口

TemporalAdjuster功能:

  • 时间调节器,将时间调节到下个周的周日,下一个工作日,或者本月中的某一天,这个时候可以使用调节器TemporalAdjuster来更方便的处理日期。
  • 因为java.time的时间类都是不可变,因此需要调整时间时,可调用该方法实现。

设计思想:

  • 通过with方法传入TemporalAdjuster接口的实现类对象,就可以进行修改。
  • 实现类对象是由TemporalAdjusters类的静态方法来提供
  • 注意TemporalAdjusters类并不是TemporalAdjuster接口的实现类,只不过TemporalAdjusters类中的静态方法实现了TemporalAdjuster接口,并将实现的对象返回了而已。
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class Main {
    //TemporalAdjusters枚举类用法实例
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        //返回当前月份的第一天的日期
        System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
        //返回当前月份的最后一天
        System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));
        //返回下一个月的第一天的日期
        System.out.println(now.with(TemporalAdjusters.firstDayOfNextMonth()));
    }
}

自定义时间调节器

要自定义日期时间的更改逻辑,可以通过实现TemporalAdjuster类接口的方式来完成。

步骤:

  • 创建类实现TemporalAdjuster接口
  • 实现TemporalAdjuster中的 adjusterInto()方法,传入一个日期时间对象,完成逻辑之后返回日期事件对象。
  • 通过with方法传入自定义调节器对象完成更改。
需求:假如员工一个月中领取工资,发薪日是每个月的15日,如果发薪日是周六、周末,则调整为周五。
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

public class Main {
    public static void main(String[] args) {
        // 封装 LocalDate 对象为2018年12月1日
        LocalDate payDay = LocalDate.of(2018, Month.DECEMBER, 15);
        // 计算 payDay 的真实发薪日是什么时候
        LocalDate realPayDay = LocalDate.from(new PayDayAdjuster().adjustInto(payDay));
        //预计的发薪日是:2018-12-15
        System.out.println("预计的发薪日是:" + payDay);
        //真实的发薪日是:2018-12-14
        System.out.println("真实的发薪日是:" + realPayDay);
    }
}

/**
 * 自定义调整器
 * 传入一个日期时间对象,判断是不是15号,如果不是就修改为15号,如果是周六或者是周日,则改为周五
 */
class PayDayAdjuster implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        // Temporal 是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();
        }
        // 将 payDay中的值改为15
        LocalDate realPayDay = payDay.withDayOfMonth(day);
        // 3、判断 readPayDay 对象中封装的星期数是不是周六或者是周日,如果是就改为上一周周五
        if (realPayDay.getDayOfWeek() == DayOfWeek.SUNDAY ||
                realPayDay.getDayOfWeek() == DayOfWeek.SATURDAY) {
            // 如果发薪日是周末则修改为上一个周五
            realPayDay = realPayDay.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        }
        return realPayDay;
    }
}

TemporalQuery接口

计划日期距离某一天特定天数差距多少天,可以自定义类实现TemporalQuery接口并且作为参数传到query方法中。

需求:计算当前时间距离下一个劳动节还有多少天?
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;

public class Main {
    public static void main(String[] args) {
        // 计算距离下一个劳动节还有多少天
        LocalDate now = LocalDate.now();
        // 调用now的query方法,然后将我们自己的实现类UtilDayQueryImpl作为参数传入
        Long day = now.query(new UtilDayQueryImpl());
        //2020-05-03
        System.out.println(now);
        //363
        System.out.println(day);
    }
}

//计算当前时间距离下一个劳动节还有多少天?
class UtilDayQueryImpl implements TemporalQuery {
    @Override
    public Long queryFrom(TemporalAccessor temporal) {
        // 1、TemporalAccessor是LocalDateTime的顶级父接口,相当于LocalDate就是这个接口,将Temporal转换为
        // 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;
    }
}
计算任意时间距离圣诞节、儿童节、劳动节相差多少天
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // 1、封装任意日期
        LocalDate date = LocalDate.of(2020, 5, 30);
        // 2、调用date 对象的query方法查询距离三个节日的差额
        Map query = date.query(new CalculatorHappyDay());
        // 3、打印结果
        System.out.println(query.get("ChristmasDay"));
        System.out.println(query.get("ChildrensDay"));
        System.out.println(query.get("LaborDay"));
    }
}

/**
 * 类型:计算任意时间距离圣诞节、儿童节、劳动节相差多少天
 * 说明:TemporalQuery的泛型类型表明要返回的类型
 *      String表示节日类型;Long表示多少天
 */
class CalculatorHappyDay implements TemporalQuery> {
    @Override
    public Map queryFrom(TemporalAccessor temporalAccessor) {
        // 1、将传入的参数转换为LocalDate对象
        LocalDate date = LocalDate.from(temporalAccessor);
        // 2、封装当年的圣诞节/儿童节/劳动节的日期时间对象
        LocalDate d1 = LocalDate.of(date.getYear(), Month.DECEMBER, 25);
        LocalDate d2 = LocalDate.of(date.getYear(), Month.JUNE, 1);
        LocalDate d3 = LocalDate.of(date.getYear(), Month.MAY, 1);
        // 3、判断date是否已经超过了d1/d2/d3
        if (date.isAfter(d1)) {
            d1 = d1.plusYears(1);
        }
        if (date.isAfter(d2)) {
            d2 = d2.plusYears(1);
        }
        if (date.isAfter(d3)) {
            d3 = d3.plusYears(1);
        }

        //计算差值
        Map result = new HashMap<>();
        result.put("ChristmasDay", ChronoUnit.DAYS.between(date,d1));
        result.put("ChildrensDay", ChronoUnit.DAYS.between(date,d2));
        result.put("LaborDay", ChronoUnit.DAYS.between(date,d3));
        return result;
    }
}

其中:

java.time.temporal.ChronoUnit

  1. 时间度量单位,枚举类,继承TemporalUnit。
  2. 它表示的是一个时间间隔用什么单位度量,比如两天的时间间隔可以用48个小时代替表示。
  3. 一般用于某时间单位的设置,加减操作

java.time.temporal.ChronoField

  1. 继承TemporalField,枚举类。
  2. 和ChronoUnit功能类似,基于TemporalUnit实现,一般用于获取不同时间域的值

 

util包的时间与time包的时间互转

旧API转time包下的API

  • Date.toInstant()
  • Date.from(Instant)
  • Calendar.toInstant()

time包与JDBC

  • date -> LocalDate
  • time -> LocalTime
  • timestamp -> LocalDateTime

util包Date转time包LocalDate

转换方案:

  1. 使用Instant类作为中介:util包中的Date类,Calendar类在java1.8都提供了一个新的方法,叫做toInstant(),可以将当前对象转换为Instant对象,通过给Instant添加时区信息之后就可以转换为LocalDate对象。
  2. 使用java.sql.Date和java.sql.TimeStamp类提供的方法进行转换。其中Date类提供toLocalDate()方法,TimeStamp类提供toLocalDateTime()方法。

toInstant()方法

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 初始化Date()对象
        Date d = new Date();
        // 1、将Date()对象转换成Instant对象
        Instant i = d.toInstant();
        // 2、Date类包含日期和时间信息,但是不提高时区信息,和Instant一样,通过Instant的anZone()来进行添加时区信息
        ZonedDateTime zonedDateTime = i.atZone(ZoneId.systemDefault());
        // 3、通过LocalDate方法转换
        LocalDate date = zonedDateTime.toLocalDate();
        // 转换之前Sun May 03 17:21:18 CST 2020
        System.out.println("转换之前" + d);
        // 转换之后2020-05-03
        System.out.println("转换之后" + date);
    }
}

java.sql.Date和java.sql.TimeStamp类转换

import java.sql.Date;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {
        // 初始化 Date对象
        Date d = new Date();
        /*
         * java.sql.Date类提供了转换为LocalDate的方法,那么可以将java.util.Date先转换为java.sql.Date
         * 通过java.sql.Date提供的方式转换为LocalDate.
         * java.sql.Date类构造的时候需要毫秒值, -> java.Util.Date类中提供了一个获取毫秒值的方法, getTime()
         * */
        java.sql.Date date = new java.sql.Date(d.getTime());
        LocalDate localDate = date.toLocalDate();
        // 转换前的java.util.Date对象是:Sun May 03 17:39:46 CST 2020
        System.out.println("转换前的java.util.Date对象是:" + d);
        // 转换前的java.sql.Date对象是:2020-05-03
        System.out.println("转换前的java.sql.Date对象是:" + date);
        // 转换前的java.time.LocalDate对象是:2020-05-03
        System.out.println("转换前的java.time.LocalDate对象是:" + localDate);
    }
    public static void transfer() {
        /*
         * sql.Date转LocalDate
         */
        // 初始化 java.sql.Date 对象
        Date d = new Date(System.currentTimeMillis());
        // java.sql.Date 自带了转换成LocalDate 的方法, toLocalDate.
        LocalDate date = d.toLocalDate();
        // 转换前的java.sql.Date对象是:2020-05-03
        System.out.println("转换前的java.sql.Date对象是:" + d);
        // 转换后的java.time.LocalDate对象是:2020-05-03
        System.out.println("转换后的java.time.LocalDate对象是:" + date);

        /*
         * sql.TimeStamp转LocalDateTime
         */
        // 初始化java.sql.Timestamp对象,  世界戳对象
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        // java.sql.Timestamp 中也自带了转换成LocalDate 的方法  toLocalDate
        LocalDateTime localDateTime = timestamp.toLocalDateTime();
        // 转换之前的java.sql.Timestamp对象是:2020-05-03 17:25:29.057
        System.out.println("转换之前的java.sql.Timestamp对象是:" + timestamp);
        // 转换之后的java.time.LocalDateTime对象是:2020-05-03T17:25:29.057
        System.out.println("转换之后的java.time.LocalDateTime对象是:" + localDateTime);
    }
}

Calendar转LocalDateTime

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

import java.time.LocalDateTime;
import java.util.Calendar;

public class Main {
    public static void main(String[] args) {
        // 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方法的参数传递,封装的月份是从0开始的,所以month要加1
        LocalDateTime localDateTime = LocalDateTime.of(year, month + 1, day, hour, minute, second);
        System.out.println(localDateTime);
    }
}

Calendar转ZonedDateTime

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

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        // 1、初始化一个Calendar对象
        Calendar calendar = Calendar.getInstance();
        // 2、Calendar对象自java1.1以后开始提供了一个方法用于获取时区对象getTimeZone()方法,要将Calendar转换为
        // ZoneedDateTime对象需要先获取到时区对象
        TimeZone timeZone = calendar.getTimeZone();
        // 3、从java1.8开始TimeZone类提供了一个方法可以获取到ZoneId -> 拿拿ZoneId对象来构建ZonedDateTime
        ZoneId zoneId = timeZone.toZoneId();
        // 4、zonedDateTime类有一个ofInstant方法。可以将Instant对象和ZoneId对象封装为一个ZonedDateTime
        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), zoneId);
        // 转换之前的Calendar对象是:java.util.GregorianCalendar[time=1588499141905,areFieldsSet=true,areAll
        // FieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",.......
        System.out.println("转换之前的Calendar对象是:" + calendar);
        // 转换之后的ZonedDateTime对象是:2020-05-03T17:45:41.905+08:00[Asia/Shanghai]
        System.out.println("转换之后的ZonedDateTime对象是:" + zonedDateTime);
    }
}

 

总结:

  • 本文基于JDK1.8,概略地介绍了java.time包下的API组织结构。
  • 日期时间:LocalDate, LocalTime, LocalDateTime
  • 年月日(枚举类):Year, YearMonth, MonthDay, Month, DayOfWeek
  • 时间间隔:Instant, Duration, Period
  • 时区:ZonedDateTime, ZoneId, TimeZone, ZoneOffest, OffsetTime, OffsetDateTime
  • 时间显示格式:parse, format, ofLocalizedDate, ofPattern方法
  • 时间调节器
  • util时间和time时间的相互转换

参考资料:

  • 黑马程序员:https://www.bilibili.com/video/BV1aZ4y1j78G
  • Java Core II

 

你可能感兴趣的:(Java,Java,时间日期,JDK1.8)