JDK8新的日期和时间 API

JDK8新的日期和时间 API

目标

  • 了解旧版日期和时间API存在的问题
  • 新日期时间API介绍
  • 掌握JDK8的日期和时间类
  • 掌握JDK8的时间格式化与解析
  • 掌握JDK8的Instant时间戳
  • 了解JDK8的计算日期时间差类
  • 了解JDK8设置日期时间的分区

旧版日期时间 API 存在的问题

  1. 设计很差:在java.util和java.sql的包中都有日期类,java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期。此外格式化和解析的类在java.text包中定义。
  2. 非线程安全:java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。时间格式化和解析都是线程不安全的。
  3. 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但它们同样存在上述所有的问题

新日期时间 API介绍

jdk8中增加了一套全新的日期时间API,这套API设计合理,是线程安全的。新的日期及时间API位于java.time包中,下面是一些关键类。

  • LocalDate:表示日期,包含年月日,格式为 2019-10-16
  • LocalTime:表示时间,包含时分秒,格式为 16:38:54.15849300
  • LocalDateTime:表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
  • DateTimeFormatter:日期时间格式化类
  • Instant:时间戳,表示一个特定的时间瞬间。
  • Duration:用于计算2个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZonedDateTime:包含时区的时间

Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366天。此外Java8还提供了4种其它历法,分别是:

  • ThaiBuddhistDate:泰国佛教历
  • MinguoDate:中华民国历
  • JspaneseDate:日本历
  • HijrahDate:伊斯兰历

JDK8的日期和时间类

LocalDate、LocalTime、LocalDateTime 类的实例是不可变对象,分别使用ISO-8061日历系统的日期、时间、日期和时间。它们提供了简单的日期和时间,并不包含当前的时间信息,也不包含与时区相关的信息。

package com.tij.stream;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
 * @author hhc19
 * @date 2023/6/17 22:04
 * @description
 */
public class Test02 {

    public static void main(String[] args) {
        testLocalDate();
        System.out.println("--------------------------");
        testLocalTime();
        System.out.println("--------------------------");
        testLocalDateTime();
        System.out.println("--------------------------");
        testLocalDateTime2();
    }

    private static void testLocalDate() {
        // LocalDate:表示日期,有年月日
        LocalDate date = LocalDate.of(2018, 8, 8);
        System.out.println("date = " + date);

        LocalDate now = LocalDate.now();
        System.out.println("now = " + now);

        System.out.println(now.getYear());
        System.out.println(now.getMonthValue());
        System.out.println(now.getDayOfMonth());
    }

    private static void testLocalTime() {
        // LocalTime:表示时间,有时分秒
        // 得到指定时间
        LocalTime time = LocalTime.of(13, 26, 39);
        System.out.println("time = " + time);
        
        // 得到当前时间
        LocalTime now = LocalTime.now();
        System.out.println("now = " + now);

        // 获取时间信息
        System.out.println(now.getHour());
        System.out.println(now.getMinute());
        System.out.println(now.getSecond());
        System.out.println(now.getNano());
    }

    private static void testLocalDateTime() {
        // LocalDateTime = LocalDate + LocalTime,包含 年月日时分秒
        LocalDateTime dateTime = LocalDateTime.of(2018, 7, 12, 13, 28, 59);
        System.out.println("dateTime = " + dateTime);

        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = " + now);

        System.out.println(now.getYear());
        System.out.println(now.getMonthValue());
        System.out.println(now.getDayOfMonth());
        System.out.println(now.getHour());
        System.out.println(now.getMinute());
        System.out.println(now.getSecond());
        System.out.println(now.getNano());
    }
}

对日期时间的修改,对已存在的LocalDate对象,创建它的修改版,最简单的方式是使用withAttribute方法,withAttribute方法会创建对象的一个副本,并按照需求修改它的属性。
以下所有的方法都返回了一个修改了属性后的新对象,它们不会影响原来的对象。

// 修改时间:修改后返回新的时间对象,和String一样,保护性复制
private static void testLocalDateTime2() {
    LocalDateTime now = LocalDateTime.now();
    System.out.println("now = " + now);

    // 修改日期时间
    LocalDateTime setYear = now.withYear(2078);
    System.out.println("修改年份 = " + setYear);

    System.out.println("修改月份:" + now.withMonth(6));
    System.out.println("修改小时:" + now.withHour(9));
    System.out.println("修改分钟:" + now.withMinute(11));

    // 在当前对象的基础上加上减去指定的时间
    LocalDateTime localDateTime = now.plusDays(5);
    System.out.println("5天后:" + localDateTime);
    System.out.println("now == localDateTime:" + (now == localDateTime));
    System.out.println("10年后:" + now.plusYears(10));
    System.out.println("20月后:" + now.plusMonths(20));

    System.out.println("20年前:" + now.minusYears(20));
    System.out.println("5月前:" + now.minusMinutes(5));
    System.out.println("100天前:" + now.minusDays(100));
}

日期时间的比较

import java.time.LocalDateTime;

public class TestEquals {
    public static void main(String[] args) {
        // 在jdk8中,LocalDate类中使用 isAfter、isBefore、isEquals方法来比较两个日期,可以直接进行比较
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime dateTime = LocalDateTime.of(2022, 6, 8);

        System.out.println(now.isBefore(dateTime)); // true
        System.out.println(now.isAfter(dateTime)); // false
        System.out.println(now.isEqual(dateTime)); // false
    }
}

JDK8的时间格式化与解析

通过 java.time.format.DateTimeFormatter类可以进行日期时间解析与格式化。

// 日期格式化
private static void testDateTimeFormatter() {
    // 得到当前时间
    LocalDateTime now = LocalDateTime.now();

    // 格式化
    // 指定时间的格式
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    // 将日期格式化为字符串
    String format = now.format(formatter);
    System.out.println("format = " + format);
    // System.out.println("date = " + LocalDate.now().format(formatter)); java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    System.out.println("date = " + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); // 2023-06-18
    // JDK自带的时间格式
    System.out.println("date = " + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); // 2023-06-18

    // 将字符串解析为日期时间
    LocalDateTime parse = LocalDateTime.parse("2023-06-18 10:48:12", formatter);
    System.out.println("parse = " + parse);

    // 测试是否线程安全
    for (int i = 0; i < 50; i++) {
        new Thread(() -> {
            LocalDateTime parse2 = LocalDateTime.parse("2023-06-18 10:48:12", formatter);
            System.out.println("parse = " + parse2);
        }).start();
    }
}

JDK8的Instant类

Instant 时间戳/时间线,内部保存了从 1970年1月1日 00:00:00 以来的秒和纳秒

// 时间戳
private static void testInstant() {
    // Instant 内部保存了秒和纳秒,一般不是给用户使用的,而是方便我们程序做一些统计的
    // 主要是为了操作 秒、纳秒、毫秒
    Instant now = Instant.now(); // 2023-06-18T03:00:39.215Z
    System.out.println("当前时间戳:" + now);
    // 获取从 1970年1月1日 00:00:00的秒、毫秒、纳秒
    System.out.println("now.getNano() = " + now.getNano()); // 纳秒
    System.out.println("now.getEpochSecond() = " + now.getEpochSecond()); // 秒
    System.out.println("now.toEpochMilli() = " + now.toEpochMilli()); // 毫秒
    System.out.println("System.currentTimeMillis() = " + System.currentTimeMillis());

    Instant instant = Instant.ofEpochMilli(5);
    System.out.println("instant = " + instant);

    // 修改方法:plus、plusMillis、plusNanos、plusSeconds、with,minus和plus一致
    Instant add = now.plusSeconds(5);
    System.out.println("add = " + add);
    Instant minus = now.minusSeconds(20);
    System.out.println("minus = " + minus);
}

JDK8计算日期时间差类

Duration/Period类:计算日期时间差。

  1. Duration:用于计算2个时间(LocalTime,时分秒)的距离
  2. Period:用于计算2个日期(LocalDate,年月日)的距离
// Duration、Period:计算日期时间差
private static void testDuration() {
    LocalTime now = LocalTime.now();
    LocalTime time = LocalTime.of(10, 15, 25);
    // Duration 用于计算 时间(LocalTime)的距离
    // 让后面的时间减去前面的时间
    Duration duration = Duration.between(time, now);
    System.out.println("相差天:" + duration.toDays());
    System.out.println("相差小时:" + duration.toHours());
    System.out.println("相差分钟:" + duration.toMinutes());
    System.out.println("相差秒:" + duration.getSeconds());
    System.out.println("相差毫秒:" + duration.toMillis());
    System.out.println("相差纳秒:" + duration.toNanos());

    LocalDate now2 = LocalDate.now();
    LocalDate date = LocalDate.of(1985, 9, 22);

    // Period用于计算 时间(LocalDate)的距离
    Period period = Period.between(date, now2);
    System.out.println("相差年:" + period.getYears());
    System.out.println("相差月:" + period.getMonths());
    System.out.println("相差日:" + period.getDays());
}

JDK8的时间校正器

有时候我们可能需要获取例如:将日期调整到“下一个月的第一天”等操作。可以通过时间校正器来进行。

  • TemporalAdjuster:时间校正器
  • TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现
/**
 * TemporalAdjuster类:自定义调整时间
 */
private static void testTemporalAdjuster() {
    LocalDateTime now = LocalDateTime.now();

    // 得到下个月的第一天
    // Temporal 就是时间,LocalDate、LocalTime、LocalDateTime 都是它的实现类
    // 自定义时间调整器
    TemporalAdjuster firstWeekDayOfNextMonth = temporal -> {
        // temporal 要调整的时间
        LocalDateTime dateTime = (LocalDateTime) temporal;
        LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1); // 下个月的第一天
        System.out.println("nextMonth = " + nextMonth);
        return nextMonth;
    };

    // JDK中自带了很多时间调整器
    System.out.println("nextYear = " + now.with(TemporalAdjusters.firstDayOfNextYear()));

    LocalDateTime nextMonth = now.with(firstWeekDayOfNextMonth);
    System.out.println("nextMonth = " + nextMonth);
}

JDK8设置日期时间的时区

Java8 中加入了对时区的支持,LocalDate、LocalTime、LocalDateTime都是不带时区的,带时区的日期时间类分别:ZonedDate、ZonedTime、ZonedDateTime。

其中每个时区都对应着ID,ID的格式为“区域/城市”。例如:Asia/Shanghai等。

ZoneId:该类中包含了所有的时区信息。

// 设置日期时间的时区
private static void testZoned() {
    // 1、获取所有的时区ID
    // ZoneId.getAvailableZoneIds().forEach(System.out::println);

    // 2、操作带时区的类
    // now(Clock.systemUTC()):创建世界标准时间,0时区
    final ZonedDateTime zonedDateTimeFromClock = ZonedDateTime.now(Clock.systemUTC());
    System.out.println("zonedDateTimeFromClock = " + zonedDateTimeFromClock); // 2023-06-18T04:30:38.574Z

    // 创建一个当前时间(不带时区,获取计算机的当前时间)
    LocalDateTime now = LocalDateTime.now(); // 中国使用的是东八区的时区,比标准时间早8个小时
    System.out.println("now = " + now);

    // 创建一个带有时区的ZonedDateTime
    // now():使用计算机默认的时区:创建日期和时间
    final ZonedDateTime dateTime = ZonedDateTime.now();
    System.out.println(dateTime); // 2023-06-18T12:39:09.654+08:00[Asia/Shanghai]

    // 使用指定的时区创建日期时间
    final ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Africa/Nairobi"));
    System.out.println(zonedDateTime); // 2023-06-18T07:33:22.824+03:00[Africa/Nairobi]

    // 修改时区
    // withZoneSameInstant:更改时区,也更改时间
    ZonedDateTime withZoneSameInstant = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
    System.out.println("withZoneSameInstant = " + withZoneSameInstant); // 2023-06-18T12:42:31.678+08:00[Asia/Shanghai]

    // withZoneSameLocal:只更改时区,不更改时间
    ZonedDateTime withZoneSameLocal = zonedDateTime.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
    System.out.println("withZoneSameLocal = " + withZoneSameLocal);
}

小结

详细学习了新的日期时间相关类,LocalDate表示日期,包含年月日;LocalTime表示时间,包含时分秒;LocalDateTime = LocalDate + LocalTime。

时间的格式化和解析,通过 DateTimeFormatter 格式化器类型进行。

学习了 Instant类,方便操作秒和纳秒,一般给程序使用。学习了Duration/Period 计算日期或时间的距离,还使用了时间调整器 TemporalAdjuster 方便的调整时间。
学习了带时区的3个类:ZonedDate/ZonedTime/ZonedDateTime

JDK8新的日期和时间API的优势:

  1. 新版的日期和时间API中,日期和时间对象是不可变的。操纵的日期不会影响老值,而是新生成一个实例。
  2. 新的API提供了两种不同的时间表示方式,有效地区分了人和机器的不同需求
  3. TemporalAdjuster可以更精确的操纵日期,还可以自定义日期调整器
  4. 是线程安全的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值