浅析Java8新特性-新的日期和时间API:起初时间存在的问题(非线程安全、设计乱、时区处理麻烦)、日期时间(LocalDate/LocalTime/LocalDateTime)、时间戳(Instan

一、Java8 之前时间存在的问题

  Java 8 (又称为 jdk1.8) 是 Java 语言开发的一个主要版本,它支持函数式编程,新的日期 API,新的Stream API 等。Java 8通过发布新的 Date-Time API (JSR 310) 来进一步加强对日期与时间的处理。在旧版的 Java 中,我们使用的是 SimpleDateFormat 对日期进行格式化,日期时间 API(Date,calendar) 存在诸多问题,其中有:

1、非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

2、设计很差 − Java的日期/时间类的定义并不一致,在 java.util 和 java.sql 的包中都有日期类,此外用于格式化和解析的类在 java.text 包中定义。java.util.Date 同时包含日期和时间,而 java.sql.Date 仅包含日期,将其纳入 java.sql 包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

3、时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此 Java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题。

  Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

(1)Local(本地) − 简化了日期时间的处理,没有时区的问题。

(2)Zoned(时区) − 通过制定的时区处理日期时间。

二、Java8 新特性之新的时间和日期的使用

  传统的时间 API 存在线程安全的问题,在多线程开发中必须要上锁,所以 java8 现在为我们提供了一套全新的时间日期 API。

  Java 8 的日期和时间类包含:日期( LocalDate)、时间(LocalTime)、时刻(Instant)、过程(Duration) 以及时钟(clock),这些类都包含在 java.time 包中,Java 8 新的时间 API 的使用方式,包括创建、格式化、解析、计算、修改。

Instant:时间戳
Duration:持续时间,时间差
LocalDate:只包含日期,比如:2021-09-02
LocalTime:只包含时间,比如:23:12:10
LocalDateTime:包含日期和时间,比如:2019-02-02 23:14:21
Period:时间段
ZoneOffset:时区偏移量,比如:+8:00
ZonedDateTime:带时区的时间
Clock:时钟,比如获取目前美国纽约的时间

1、LocalDate、LocalTime、LocalDateTime

  LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601 (ISO-8601 日历系统是国际化组织制定的现代化公民的日期和时间的表达法)日历系统的日期、时间、日期和时间。

  从名称也可以看出来,第一种表示日期 年月日,第二种表示时间 时分秒,第三种表示年月日时分秒。

  由于的实例是不可变的对象,所以我们如果对时间对象进行更改,包括修改时间,加减时间都需要重新返回一个新的实例。

// 获取当前时间
LocalDate localDate = LocalDate.now();
// 获取指定日期时间
LocalDateTime localDateTime = LocalDateTime.of(2020,06,02,12,22,23);
// 当前日期+1天
LocalDate localDate =LocalDate.now();
LocalDate tomorrow =  localDate.plusDays(1);

// 对日期时间进行加减
/**
* 对日期时间进行加操作,使用 localDateTime.plus***(num)
* 对日期时间进行减操作,使用 localDateTime.minus***(num)
* 修改不限于,年、月、日、时、分、秒、纳秒
*/
LocalDateTime localDateTime2 = localDateTime.plusMonths(2);
System.out.println("指定时间添加2月:"+localDateTime2);

LocalDateTime localDateTime3 = localDateTime.minusDays(2);
System.out.println("指定时间减少2天:"+localDateTime3);

  对日期时间进行加操作,使用 localDateTime.plusXXX(num)

  对日期时间进行减操作,使用 localDateTime.minusXXX(num)

  修改不限于,年、月、日、时、分、秒、纳秒

2、Instant: 时间戳(以Unix 元年 : 1970-01-01 00:00:00 到某个时间之间的毫秒数)

//获取当前时间戳   时间戳已UTC 时间展示,与中国时间差距8小时
Instant instant = Instant.now();
//如果想要获取中国时间 可以通过设置偏移量来获取 OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));

  使用Instant获取当前时间戳默认为UTC格式时间,我们如果想要获取中国时间的话,必须给instant设置偏移量,instant.atOffset(ZoneOffset.ofHours(8));可以设置偏移量为加8小时,即为中国时间。也可设置负值,则为减8小时。

//toEpochMilli 获取毫秒数
long l = instant.toEpochMilli();

  获取毫秒数(时间戳):以前来获取当前时间戳是用:new Date().getTime() 以及 System.currentTimeMillis获取。现在可用使用instant.toEpochMilli()来获取,他们三个获取的时间戳是一样的。

3、Duration、Period:计算两个日期时间的时间差

(1)Duration获取2个时间之间的间隔(LocalDateTime、Instant)

  使用Duration.between(instant, instant1)获取两个时间时间差返回一个 Duration对象,可以通过对象内部的实例方法获取时间间隔。

Duration between = Duration.between(instant, instant1);
System.out.println("毫秒:"+between.toMillis());
System.out.println("秒:"+between.getSeconds());

  Duration不仅可以获取Instant时间的间隔,获取LocalDateTime 的时间间隔也是可以的。

LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime localDateTime1 = LocalDateTime.of(2020,09,18,15,02,03);

Duration between1 = Duration.between(localDateTime, localDateTime1);
System.out.println("LocalDateTime间隔小时:"+between1.toHours());

(2)Period获取两个时间之间的间隔 (LocalDate)

Period between2 = Period.between(LocalDate.now(), LocalDate.of(2020, 07, 06));
System.out.println("日期间隔天数:"+between2.getDays());

4、TemporalAdjuster:时间矫正器。

  有时我们可能需要获取一个周末,或者下一个工作日等时间,这里 java8 就为我们提供了一个时间校正器,让我们对时间进行校准。

  TemporalAdjusters:该类通过静态方法提供了大量的常用的TemporalAdjuster的实现供我们使用。

  在localDateTime中,有一个with方法,其中可以让我们去写一TemporalAdjuster接口,而TemporalAdjusters类中,有许多常用的方法

//时间校正器
    @Test
    public void testTemporalAdjuster(){
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("当前时间:"+localDateTime);
        LocalDateTime localDateTime1 = localDateTime.withDayOfMonth(10);
        System.out.println("手动指定日期时间:"+localDateTime1);

        //获取当月第一天
        LocalDateTime with = localDateTime.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("使用TemporalAdjuster获取当月第一天:"+with);

        System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfMonth()));
         //获取下一年的第一天
        System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfNextYear()));
         //获取年中第一天
        System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfYear()));
         //获取月中最后一天
        System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfMonth()));
         //获取下个星期一
        System.out.println(localDateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
    }

  TemporalAdjusters 包含许多静态方法,可以直接调用,以下列举一些:

方法名 描述
dayOfWeekInMonth 返回同一个月中每周的第几天
firstDayOfMonth 返回当月的第一天
firstDayOfNextMonth 返回下月的第一天
firstDayOfNextYear 返回下一年的第一天
firstDayOfYear 返回本年的第一天
firstInMonth 返回同一个月中第一个星期几
lastDayOfMonth 返回当月的最后一天
lastDayOfNextMonth 返回下月的最后一天
lastDayOfNextYear 返回下一年的最后一天
lastDayOfYear 返回本年的最后一天
lastInMonth 返回同一个月中最后一个星期几
next / previous 返回后一个/前一个给定的星期几
nextOrSame / previousOrSame 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回

5、时间格式化工具:DateTimeFormatter

  DateTimeFormatter是专门为LocalDateTime进行格式化的工具类,内置了很多基本格式,也可自定义时间格式化公式。写法也比较特别,可以是formatter.format(dateTime),也可以是dateTime.format(formatter)

//本身内置了很多格式化格式
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 也可自定义格式
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy年MMdd hh:mm:ss");

6、LocalDateTime 与 Date 互转

  LocalDateTime 与 Date 互转主要是利用两者共有的属性 Instant 进行相互转换。

(1)LocalDateTime 转 Date:

  LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

  或者 date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

(2)Date 转 LocalDateTime :

  Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

@Test
public void LocalDateToDate(){
    Date date = new Date();
    LocalDateTime localDateTime1 = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

    LocalDateTime localDateTime = LocalDateTime.now();
    Date from = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    System.out.println("LocalDateTime转换成date:"+from);
    SimpleDateFormat ss = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(ss.format(from));
}

7、其他一些常用的方法

(1)比较两个时间的先后

LocalDate date15 = LocalDate.of(2021,9,31);
date.isAfter(date15); // false
date.isBefore(date15); // true

(2)MonthDay类的使用

  MonthDay只包含月日信息,可以用于存放类似于生日,结婚纪念日等信息。

  比如:生日检查或账单检查,我们在购物时注册会员时都会有生日祝福,例如,用户的生日为1996-09-10,如果今天是2021-09-10,那么今天就是用户的生日(按公历/身份证日期来算),那么通过java8新的日期库,我们该如何来进行实现呢?

LocalDate birthday = LocalDate.of(1996, 09, 10);
MonthDay birthdayMd = MonthDay.of(birthday.getMonth(), birthday.getDayOfMonth());
MonthDay today = MonthDay.from(LocalDate.of(2021, 09, 10));
System.out.println(today.equals(birthdayMd));
// 输出结果为 :true

  这里用到了:

  Java中MonthDay类的 from() 方法从时间对象获取MonthDay的实例,

  Java中MonthDay类的of(Month month,int dayOfMonth)方法用于获取MonthDay的实例

MonthDay monthday = MonthDay.of(9, 18);  // 09-18
MonthDay date = MonthDay.from(ZonedDateTime.now()); // 09-18
上一篇:Java日期时间处理总结


下一篇:java1.8的localDateTime日期操作(转载)