• 迁移到新的 Java 8 日期时间 API


    1. 概述

    在本教程中,您将学习如何重构代码以利用 Java 8 中引入的新日期时间 API。

    2. 新 API 概览

    在 Java 中使用日期过去很困难。JDK提供的旧日期库仅包含三个类:java.util.Date,java.util.Calendarjava.util.Timezone

    这些仅适用于最基本的任务。对于任何远程复杂的内容,开发人员必须使用第三方库或编写大量自定义代码。

    Java 8引入了一个全新的Date Time APIjava.util.time.*),它大致基于流行的Java库JodaTime。这个新的 API 极大地简化了日期和时间处理,并修复了旧日期库的许多缺点。

    1.1. 接口清晰度

    新 API 的第一个优点是清晰——API 非常清晰、简洁且易于理解。它在旧库中没有发现很多不一致之处,例如字段编号(在日历月中从零开始,但星期几是从 1 开始的)。

    1.2. 接口灵活性

    另一个优点是灵活性 - 使用多种时间表示形式。旧的日期库只包含一个时间表示类 - java.util.Date,尽管它的名字,实际上是一个时间戳。它仅存储自 Unix 纪元以来经过的毫秒数。

    新的 API 有许多不同的时间表示形式,每种表示形式都适用于不同的用例:

    • Instant  – 表示时间点(时间戳)
    • LocalDate  – 表示日期(年、月、日)
    • LocalDateTime – 与LocalDate 相同,但包括具有纳秒精度的时间
    • OffsetDateTime – 与本地日期时间相同,但具有时区偏移量
    • LocalTime  – 具有纳秒精度且没有日期信息的时间
    • ZonedDateTime – 与OffsetDateTime 相同,但包括时区 ID
    • OffsetLocalTime – 与LocalTime 相同,但具有时区偏移量
    • MonthDay  – 月和日,没有年或时间
    • YearMonth– 月和年,没有日期或时间
    • Duration –以秒,分钟和小时表示的时间量。具有纳秒级精度
    • Period  – 以天、月和年表示的时间量

    1.3. 不变性和线程安全

    另一个优点是 Java 8 日期时间 API 中的所有时间表示都是不可变的,因此是线程安全的。

    所有可变方法都返回一个新副本,而不是修改原始对象的状态。

    java.util.Date这样的旧类不是线程安全的,并且可能会引入非常微妙的并发错误。

    1.4. 方法链

    所有变异方法都可以链接在一起,允许在一行代码中实现复杂的转换。

    1. ZonedDateTime nextFriday = LocalDateTime.now()
    2. .plusHours(1)
    3. .with(TemporalAdjusters.next(DayOfWeek.FRIDAY))
    4. .atZone(ZoneId.of("PST"));

    2. 示例

    以下示例将演示如何使用新旧 API 执行常见任务。

    获取当前时间

    1. // Old
    2. Date now = new Date();
    3. // New
    4. ZonedDateTime now = ZonedDateTime.now();

    表示特定时间

    1. // Old
    2. Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime();
    3. // New
    4. LocalDate birthDay = LocalDate.of(1990, Month.DECEMBER, 15);

    提取特定字段

    1. // Old
    2. int month = new GregorianCalendar().get(Calendar.MONTH);
    3. // New
    4. Month month = LocalDateTime.now().getMonth();

    加减时间

    1. // Old
    2. GregorianCalendar calendar = new GregorianCalendar();
    3. calendar.add(Calendar.HOUR_OF_DAY, -5);
    4. Date fiveHoursBefore = calendar.getTime();
    5. // New
    6. LocalDateTime fiveHoursBefore = LocalDateTime.now().minusHours(5);

    更改特定字段

    1. // Old
    2. GregorianCalendar calendar = new GregorianCalendar();
    3. calendar.set(Calendar.MONTH, Calendar.JUNE);
    4. Date inJune = calendar.getTime();
    5. // New
    6. LocalDateTime inJune = LocalDateTime.now().withMonth(Month.JUNE.getValue());

    截断

    截断将重置所有小于指定字段的时间字段。在下面的示例中,分钟和以下所有内容都将设置为零

    1. // Old
    2. Calendar now = Calendar.getInstance();
    3. now.set(Calendar.MINUTE, 0);
    4. now.set(Calendar.SECOND, 0);
    5. now.set(Calendar.MILLISECOND, 0);
    6. Date truncated = now.getTime();
    7. // New
    8. LocalTime truncated = LocalTime.now().truncatedTo(ChronoUnit.HOURS);

    时区转换

    1. // Old
    2. GregorianCalendar calendar = new GregorianCalendar();
    3. calendar.setTimeZone(TimeZone.getTimeZone("CET"));
    4. Date centralEastern = calendar.getTime();
    5. // New
    6. ZonedDateTime centralEastern = LocalDateTime.now().atZone(ZoneId.of("CET"));

    获取两个时间点之间的时间跨度

    1. // Old
    2. GregorianCalendar calendar = new GregorianCalendar();
    3. Date now = new Date();
    4. calendar.add(Calendar.HOUR, 1);
    5. Date hourLater = calendar.getTime();
    6. long elapsed = hourLater.getTime() - now.getTime();
    7. // New
    8. LocalDateTime now = LocalDateTime.now();
    9. LocalDateTime hourLater = LocalDateTime.now().plusHours(1);
    10. Duration span = Duration.between(now, hourLater);

    时间格式化和解析

    DateTimeFormatter 是旧的 SimpleDateFormat 的替代品,它是线程安全的并提供附加功能。

    1. // Old
    2. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    3. Date now = new Date();
    4. String formattedDate = dateFormat.format(now);
    5. Date parsedDate = dateFormat.parse(formattedDate);
    6. // New
    7. LocalDate now = LocalDate.now();
    8. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    9. String formattedDate = now.format(formatter);
    10. LocalDate parsedDate = LocalDate.parse(formattedDate, formatter);

    一个月中的天数

    1. // Old
    2. Calendar calendar = new GregorianCalendar(1990, Calendar.FEBRUARY, 20);
    3. int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    4. // New
    5. int daysInMonth = YearMonth.of(1990, 2).lengthOfMonth();

    3. 与遗留代码交互

    在许多情况下,用户可能需要确保与依赖于旧日期库的第三方库的互操作性。

    在 Java 8 中,旧的日期库类已经扩展为从新日期 API 将它们转换为相应对象的方法。
    新类提供类似的功能。

    1. Instant instantFromCalendar = GregorianCalendar.getInstance().toInstant();
    2. ZonedDateTime zonedDateTimeFromCalendar = new GregorianCalendar().toZonedDateTime();
    3. Date dateFromInstant = Date.from(Instant.now());
    4. GregorianCalendar calendarFromZonedDateTime = GregorianCalendar.from(ZonedDateTime.now());
    5. Instant instantFromDate = new Date().toInstant();
    6. ZoneId zoneIdFromTimeZone = TimeZone.getTimeZone("PST").toZoneId();

    4. 结论

    在本文中,我们探讨了 Java 8 中可用的新日期时间 API。与已弃用的 API 相比,我们查看了它的优势,并使用多个示例指出了差异。

    请注意,我们几乎没有触及新日期时间 API 功能的表面。请务必通读官方文档,以发现新 API 提供的所有工具。

    代码示例可以在GitHub 项目中找到。

  • 相关阅读:
    H2/H∞半车悬架控制仿真分析
    Minecraft 1.16.5模组开发(五十四) 方块探测器(Detector)
    11、voc转yolo数据集、训练集验证集划分、修改xml文件
    Go 单元测试之mock接口测试
    JS基础知识
    PostgreSQL 17新特性之登录事件触发器
    【0基础前端】CSS-C3总结详细笔记包含代码块从入门到高阶通俗易懂
    一种优雅的Git分支实践
    [书籍翻译]12周撰写期刊文章 学术出版成功指南——第 5 周:回顾相关文献
    java源码系列:HashMap底层存储原理详解——5、技术本质-原理过程-算法-取模会带来一个什么问题?什么是哈希冲突?为什么要用链表?
  • 原文地址:https://blog.csdn.net/allway2/article/details/128031228