Java 8 到 Java 21 系列之 新日期时间API:精确的时间管理(Java 8)

Java 8 到 Java 21 系列之 新日期时间API:精确的时间管理(Java 8)

系列目录


引言

自Java 8以来,Java引入了一个全新的日期和时间处理API——java.time包,它极大地改进了之前Date和Calendar类的不足。本文将介绍这一新API的核心特性,并通过示例展示如何在Java中进行精确的时间管理。


1 背景

在Java 8之前,处理日期和时间的方式存在诸多问题,如线程不安全性、设计不佳等。为了解决这些问题,Java团队推出了java.time包,提供了更加现代化、线程安全且易于使用的API。

2 核心类概览

2.1 核心类介绍

类别描述
LocalDate表示不含时区的日期(年-月-日)
LocalTime表示不含日期和时区的时间(小时-分钟-秒)
LocalDateTime结合LocalDate和LocalTime,表示不含时区的日期和时间
ZonedDateTime带时区的日期和时间
Instant表示时间线上的一个点,通常用于机器时间戳
  1. LocalDate: 表示不含时区的日期。
LocalDate today = LocalDate.now();
System.out.println("Today's date: " + today);
  1. LocalTime: 表示不含时区的时间。
LocalTime now = LocalTime.now();
System.out.println("Current time: " + now);
  1. LocalDateTime: 组合了日期和时间,但不包含时差和时区信息。
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("Current date and time: " + dateTime);
  1. ZonedDateTime: 最完整的日期时间,包含时区和相对UTC或格林威治的时差。
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("Current date and time in Shanghai: " + zonedDateTime);
  1. Instant: 表示时间线上的一个瞬时点,通常用来记录事件发生的时间戳。
Instant instant = Instant.now();
System.out.println("Current timestamp (Epoch seconds): " + instant.getEpochSecond());
System.out.println("Current timestamp (Nanos): " + instant.getNano());

2.2 示例代码

LocalDate使用测试

import org.junit.jupiter.api.Test;
import java.time.*;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class DateTimeApiTest {

    @Test
    public void testLocalDate() {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        System.out.println("今天的日期: " + today);

        // 创建指定日期
        LocalDate specificDate = LocalDate.of(2025, 4, 6);
        System.out.println("指定日期: " + specificDate);

        // 加一天
        LocalDate tomorrow = today.plusDays(1);
        System.out.println("明天的日期: " + tomorrow);

        // 减去一个月
        LocalDate lastMonth = today.minusMonths(1);
        System.out.println("上个月的今天: " + lastMonth);

        // 检查获取到的日期是否正确
        assertEquals(LocalDate.of(2025, 4, 6), specificDate);
        assertEquals(today.plusDays(1), tomorrow);
        assertEquals(today.minusMonths(1), lastMonth);
    }
}

执行结果
在这里插入图片描述

LocalTime使用测试

@DisplayName("测试时间")
    @Test
    public void testLocalTime() {
        // 获取当前时间
        LocalTime now = LocalTime.now();
        System.out.println("当前时间: " + now);

        // 创建指定时间
        LocalTime specificTime = LocalTime.of(14, 30);
        System.out.println("指定时间: " + specificTime);

        // 增加一个小时
        LocalTime nextHour = now.plusHours(1);
        System.out.println("一小时后的时间: " + nextHour);

        // 减少十分钟
        LocalTime tenMinutesAgo = now.minusMinutes(10);
        System.out.println("十分钟前的时间: " + tenMinutesAgo);

        // 验证时间是否正确
        assertEquals(LocalTime.of(14, 30), specificTime);
        assertEquals(now.plusHours(1).toString().substring(0, 8), nextHour.toString().substring(0, 8));
        assertEquals(now.minusMinutes(10).toString().substring(0, 8), tenMinutesAgo.toString().substring(0, 8));
    }

执行结果
在这里插入图片描述

LocalDateTime使用测试

 @DisplayName("测试日期时间")
    @Test
    public void testLocalDateTime() {
        // 获取当前日期和时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前日期和时间: " + now);

        // 创建指定日期和时间
        LocalDateTime specificDateTime = LocalDateTime.of(2025, 4, 6, 14, 30);
        System.out.println("指定日期和时间: " + specificDateTime);

        // 增加三天
        LocalDateTime threeDaysLater = now.plusDays(3);
        System.out.println("三天后的日期和时间: " + threeDaysLater);

        // 减少一年
        LocalDateTime oneYearAgo = now.minusYears(1);
        System.out.println("一年前的日期和时间: " + oneYearAgo);

        // 验证日期和时间是否正确
        assertEquals(LocalDateTime.of(2025, 4, 6, 14, 30), specificDateTime);
        assertEquals(now.plusDays(3), threeDaysLater);
        assertEquals(now.minusYears(1), oneYearAgo);
    }

执行结果
在这里插入图片描述

ZonedDateTime测试用例

    @DisplayName("测试带时区的日期时间")
    @Test
    public void testZonedDateTime() {
        // 获取上海时区的当前日期和时间
        ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println("上海时区的当前日期和时间: " + zonedDateTime);

        // 调整时区到UTC
        ZonedDateTime utcDateTime = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC);
        System.out.println("调整到UTC时区后的日期和时间: " + utcDateTime);

        // 验证时区转换是否正确
        assertEquals(zonedDateTime.withZoneSameInstant(ZoneOffset.UTC), utcDateTime);
    }

执行结果
在这里插入图片描述

Instant使用测试

    @DisplayName("测试时间戳")
    @Test
    public void testInstant() {
        // 获取当前瞬间的时间戳
        Instant instant = Instant.now();
        System.out.println("当前时间戳(秒): " + instant.getEpochSecond());
        System.out.println("当前时间戳(纳秒): " + instant.getNano());

        // 创建一个基于Unix时间戳的Instant实例
        Instant fromTimestamp = Instant.ofEpochSecond(1609459200); // 对应于2021年1月1日00:00:00 UTC
        System.out.println("根据Unix时间戳创建的Instant实例: " + fromTimestamp);

        // 验证创建的Instant实例是否正确
        assertEquals(1609459200, fromTimestamp.getEpochSecond());
    }

测试结果
在这里插入图片描述

三、对比旧版与新版API
以下表格展示了旧版日期时间API与新版之间的主要差异:

特性Java 8 之前的旧API (java.util.Date, java.util.Calendar)Java 8 及之后的新API (java.time 包)
不可变性类是可变的,可能导致线程安全问题。类是不可变的,保证了线程安全性。
API设计API设计复杂且不直观,例如月份从0开始计数。API设计更加直观,易于理解和使用,例如月份从1开始计数。
时区支持有限的支持,处理时区复杂。提供了丰富的时区支持,包括ZonedDateTimeZoneId
格式化/解析使用SimpleDateFormat进行格式化和解析,但不是线程安全的。使用DateTimeFormatter进行格式化和解析,线程安全。
操作便捷性需要更多的代码来完成简单的日期操作,例如加减天数或比较两个日期。提供了简洁的方法来执行常见的日期操作,如plusDays(), minusDays()等。
性能创建和销毁实例频繁,尤其是在多线程环境中,性能较差。不可变对象可以被重用,减少不必要的对象创建,提高了性能。
域的概念没有明确的“域”概念,处理日期的不同部分(如年、月、日)不够灵活。引入了“域”的概念,能够更灵活地获取和修改日期的不同方面。
国际标准遵循不完全遵循ISO 8601标准。完全遵循ISO 8601标准,使得国际化应用开发更加容易。
线程安全性大多数类不是线程安全的,需要额外的同步机制。所有类都是线程安全的,无需额外同步。
扩展性对于不同历法系统的支持有限。支持全球历法系统扩展,通过java.time.chrono包提供。

四、结论

五、总结
Java 8带来的新日期时间API极大地改善了我们处理日期和时间的方式。它不仅解决了老API中的诸多问题,还提供了更强大的功能和更高的灵活性,新的日期时间API解决了旧版API中的许多问题,提供了更加直观、易用的接口。无论是简单的日期操作还是复杂的时区转换,都可以轻松实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值