0

0

Java日期时间处理 Java8新时间API使用完全指南

看不見的法師

看不見的法師

发布时间:2025-07-20 18:05:01

|

920人浏览过

|

来源于php中文网

原创

java 8 引入新的日期时间api是为了解决旧api存在的诸多问题。2. 旧api存在可变性导致多线程不安全、api设计混乱、缺乏清晰的日期时间概念划分、时区处理复杂以及非线程安全的格式化类等问题。3. 新api通过不可变类如 localdate、localtime、localdatetime 提供清晰的日期、时间、日期时间的表示,并通过 instant 和 zoneddatetime 支持精确的时区处理。4. 新api支持直观的操作如 plusdays、minushours 等,且所有操作返回新实例,确保线程安全。5. 新api提供清晰的时区支持,通过 zoneid 和 zoneddatetime 实现跨时区的时间转换和处理。

Java日期时间处理 Java8新时间API使用完全指南

Java 8 引入的全新日期时间API,在我看来,简直是Java语言在日期处理方面的一次“救赎”。它彻底改变了我们过去使用 java.util.DateCalendar 时那种心力交瘁的感觉,提供了一套更直观、更安全、也更符合实际业务需求的解决方案。如果你还在为老旧API的各种坑所困扰,那么是时候拥抱这套新API了。

Java日期时间处理 Java8新时间API使用完全指南

解决方案

新的日期时间API主要位于 java.time 包下,它以其不可变性、线程安全性以及清晰的API设计,解决了旧API的诸多顽疾。核心思想是将日期、时间、日期时间、时间戳以及时间段等概念清晰地分离,并提供了强大的时区处理能力。

我们主要会接触到 LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期时间)、Instant(时间戳)、ZonedDateTime(带时区的日期时间)、Duration(持续时间)和 Period(时间段)这些核心类。它们都提供了简洁的工厂方法来创建实例,比如 now() 获取当前时间,of() 根据指定值创建。所有的修改操作,如 plusDays()minusHours() 等,都会返回一个新的实例,而不是修改原实例,这从根本上杜绝了多线程环境下的并发问题,也让代码逻辑变得更加清晰可预测。

立即学习Java免费学习笔记(深入)”;

Java日期时间处理 Java8新时间API使用完全指南
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Duration;
import java.time.Period;
import java.time.format.DateTimeFormatter;

// 简单示例
public class DateTimeApiDemo {
    public static void main(String[] args) {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        System.out.println("今天的日期: " + today); // 示例: 2023-10-27

        // 获取当前时间
        LocalTime nowTime = LocalTime.now();
        System.out.println("现在的时间: " + nowTime); // 示例: 10:30:45.123

        // 获取当前日期时间
        LocalDateTime nowDateTime = LocalDateTime.now();
        System.out.println("现在的日期时间: " + nowDateTime); // 示例: 2023-10-27T10:30:45.123

        // 创建指定日期
        LocalDate specificDate = LocalDate.of(2023, 1, 1);
        System.out.println("指定日期: " + specificDate); // 2023-01-01

        // 日期操作:加减
        LocalDate nextWeek = today.plusWeeks(1);
        System.out.println("下周的今天: " + nextWeek);

        // 格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String formattedDateTime = nowDateTime.format(formatter);
        System.out.println("格式化后的日期时间: " + formattedDateTime);
    }
}

为什么我们需要新的日期时间API?旧API到底有哪些痛点?

说实话,在我刚接触Java那会儿,用 java.util.DateCalendar 处理日期时间,简直是一场噩梦。你总觉得它在某些地方不太对劲,但又说不上来具体是哪里。

首先,最大的痛点就是可变性java.util.Date 对象是可变的。这意味着你把一个 Date 对象传给某个方法,那个方法如果修改了这个 Date 对象,原先调用方持有的 Date 实例也会跟着改变。这在多线程环境下简直是灾难,你很难追踪到底是谁在什么时候修改了你的日期对象,导致各种意想不到的bug。想想看,一个共享的 Date 对象在不同线程里被同时修改,那结果真是不可预测。

Java日期时间处理 Java8新时间API使用完全指南

其次,API设计混乱且不直观java.util.Date 自身并没有区分日期、时间或者时间戳的概念,它就是一个表示毫秒值的类。而 java.util.Calendar,虽然提供了更细粒度的操作,但它的月份是从0开始的(0代表1月),这跟我们日常习惯完全不符,新手很容易犯错。而且,它的API用起来也比较啰嗦,比如要获取年份、月份,你需要调用 get(Calendar.YEAR)get(Calendar.MONTH),而不是直接 getYear()getMonth()。还有 SimpleDateFormat 这个老伙计,它不是线程安全的,在多线程环境下共享一个 SimpleDateFormat 实例,会抛出 NumberFormatException 或其他奇怪的错误,我为此踩过不少坑。

再来就是时区处理的复杂性。旧API对时区的支持非常有限,或者说,用起来非常别扭和容易出错。你经常会遇到时间转换不对、夏令时问题等。它没有一个清晰的、类型安全的机制来处理不同的时区,导致很多国际化应用在日期时间方面做得一团糟。

最后,就是缺乏对持续时间(Duration)和时间段(Period)的直接支持。如果你想计算两个日期之间相差多少天、多少月,或者两个时间点之间相差多少小时、多少分钟,旧API要么需要手动计算,要么需要依赖复杂的 Calendar 操作,非常不便。

Java 8 的新API正是为了解决这些问题而生。它将日期、时间、时区等概念彻底解耦,提供了不可变的类,让操作更安全、更直观,也更符合面向对象的原则。

一览AI绘图
一览AI绘图

一览AI绘图是一览科技推出的AIGC作图工具,用AI灵感助力,轻松创作高品质图片

下载

LocalDate, LocalTime, LocalDateTime:它们分别用来做什么,以及如何常用操作?

这三个类是新API中最基础也最常用的,它们的设计哲学是“本地化”,即不包含任何时区信息。这让它们在处理不涉及跨时区转换的场景时,显得格外简洁高效。

  • LocalDate: 顾名思义,它只表示一个日期,不包含时间部分,也不包含时区信息。比如“2023年10月27日”。

    • 创建:
      • 获取当前日期:LocalDate today = LocalDate.now();
      • 指定日期:LocalDate birthday = LocalDate.of(1990, 5, 15);
      • 字符串解析LocalDate parsedDate = LocalDate.parse("2023-10-27"); (默认ISO_LOCAL_DATE格式)
    • 常用操作:
      • 加减日期:today.plusDays(10); (10天后), today.minusMonths(2); (2个月前)
      • 修改特定字段:today.withYear(2024); (将年份改为2024)
      • 获取字段:today.getYear();, today.getMonth(); (返回Month枚举), today.getDayOfMonth();, today.getDayOfWeek(); (返回DayOfWeek枚举)
      • 比较:today.isBefore(birthday);, today.isAfter(birthday);, today.isEqual(birthday);
      • 判断闰年:today.isLeapYear();
    LocalDate today = LocalDate.now(); // 获取当前日期
    System.out.println("今天是: " + today);
    
    LocalDate independenceDay = LocalDate.of(1776, 7, 4); // 指定日期
    System.out.println("美国独立日: " + independenceDay);
    
    LocalDate nextMonth = today.plusMonths(1); // 增加一个月
    System.out.println("下个月的今天: " + nextMonth);
    
    System.out.println("今天是周几? " + today.getDayOfWeek());
    System.out.println("今年是否是闰年? " + today.isLeapYear());
  • LocalTime: 专门表示时间,不包含日期部分,也不包含时区信息。比如“上午10点30分20秒”。

    • 创建:
      • 获取当前时间:LocalTime now = LocalTime.now();
      • 指定时间:LocalTime meetingTime = LocalTime.of(9, 30, 0); (9点30分0秒)
      • 从字符串解析:LocalTime parsedTime = LocalTime.parse("14:30:00");
    • 常用操作:
      • 加减时间:now.plusHours(2);, now.minusMinutes(15);
      • 修改特定字段:now.withSecond(0);
      • 获取字段:now.getHour();, now.getMinute();, now.getSecond();
      • 比较:now.isBefore(meetingTime);
    LocalTime currentTime = LocalTime.now(); // 获取当前时间
    System.out.println("当前时间: " + currentTime);
    
    LocalTime lunchTime = LocalTime.of(12, 0); // 指定时间
    System.out.println("午餐时间: " + lunchTime);
    
    LocalTime inHalfAnHour = currentTime.plusMinutes(30); // 30分钟后
    System.out.println("30分钟后: " + inHalfAnHour);
  • LocalDateTime: 结合了 LocalDateLocalTime,表示日期和时间,但依然不包含时区信息。比如“2023年10月27日上午10点30分20秒”。这是我们日常开发中最常用到的一个类。

    • 创建:
      • 获取当前日期时间:LocalDateTime now = LocalDateTime.now();
      • 指定日期时间:LocalDateTime specificDateTime = LocalDateTime.of(2023, 10, 27, 10, 30, 0);
      • LocalDateLocalTime 组合:LocalDateTime combined = LocalDateTime.of(LocalDate.now(), LocalTime.now());
      • 从字符串解析:LocalDateTime parsedDateTime = LocalDateTime.parse("2023-10-27T10:30:00"); (注意中间的'T')
    • 常用操作: 它拥有 LocalDateLocalTime 的所有加减、修改、获取字段和比较操作。
      • 转换为 LocalDateLocalTimenow.toLocalDate();, now.toLocalTime();
    LocalDateTime currentDateTime = LocalDateTime.now(); // 获取当前日期时间
    System.out.println("当前日期时间: " + currentDateTime);
    
    LocalDateTime examTime = LocalDateTime.of(2023, 12, 15, 9, 0); // 指定考试时间
    System.out.println("考试时间: " + examTime);
    
    LocalDateTime nextYearSameTime = currentDateTime.plusYears(1); // 明年同一时刻
    System.out.println("明年同一时刻: " + nextYearSameTime);
    
    System.out.println("提取日期部分: " + currentDateTime.toLocalDate());
    System.out.println("提取时间部分: " + currentDateTime.toLocalTime());

    这些“Local”系列类,在处理数据库存储、日志记录等场景时非常方便,因为它们不涉及时区,可以避免很多不必要的时区转换带来的复杂性。但一旦涉及到全球化应用,或者需要精确到某个特定时区的时间点,那就得请出 InstantZonedDateTime 了。

深入理解 InstantZonedDateTime 和时区转换

当我们谈论全球化应用,或者需要一个在不同时区都能精确表示同一物理时间点的场景时,InstantZonedDateTime 就显得尤为重要了。它们是处理时区问题的核心。

  • Instant: 想象一下,这是一个在时间轴上的一个“瞬间点”,它代表了自UTC(协调世界时)1970年1月1日00:00:00以来,经过的纳秒数。它不包含任何关于日期、月份、年份或时区的概念,只是一个纯粹的、机器可读的时间戳。

    • 创建:
      • 获取当前瞬间:Instant now = Instant.now();
      • 从毫秒/秒值:Instant epochInstant = Instant.ofEpochMilli(0);
    • 用途: 非常适合用于数据库存储时间戳、记录事件发生的确切时间、或者在不同系统之间传递时间信息。因为它是一个全球统一的时间点,没有歧义。
    • 操作: 主要是一些加减操作,比如 plusSeconds(), minusMillis(), 还有和 Duration 配合使用。
    Instant currentInstant = Instant.now(); // 获取当前UTC时间戳
    System.out.println("当前Instant (UTC): " + currentInstant);
    
    // 从LocalDateTime转换为Instant,需要指定一个ZoneOffset或ZoneId
    // 假设是东八区
    LocalDateTime localDateTime = LocalDateTime.of(2023, 10, 27, 10, 30, 0);
    ZoneOffset offset = ZoneOffset.ofHours(8); // 东八区偏移量
    Instant fromLocalDateTime = localDateTime.toInstant(offset);
    System.out.println("从LocalDateTime转换的Instant (东八区): " + fromLocalDateTime);
  • ZonedDateTime: 这才是真正处理“带时区的日期时间”的类。它结合了 LocalDateTime(日期和时间)和一个 ZoneId(时区ID)。这意味着它不仅知道是哪年哪月哪日几点几分,还知道这个时间是属于哪个时区的。

    • ZoneId: 代表一个时区标识符,比如 "Asia/Shanghai", "America/New_York"。你可以通过 ZoneId.of("...")ZoneId.systemDefault() 获取。
    • ZoneOffset: 仅仅表示一个固定的UTC偏移量,比如 "+08:00"。它没有夏令时规则,也不关心地理位置
    • 创建:
      • 获取当前时区的日期时间:ZonedDateTime now = ZonedDateTime.now();
      • 获取指定时区的日期时间:ZonedDateTime parisTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
      • LocalDateTimeZoneId 组合:ZonedDateTime specificZoned = ZonedDateTime.of(LocalDateTime.of(2023, 10, 27, 10, 30), ZoneId.of("Asia/Shanghai"));
    • 操作: 拥有 LocalDateTime 的所有操作,并且增加了时区相关的操作。
      • 时区转换: zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York")); 这是最常用的操作,它会调整时间以反映在目标时区中的同一物理瞬间。
      • 转换为 Instant: zonedDateTime.toInstant();
      • 获取时区:zonedDateTime.getZone();
    // 获取系统默认时区的当前时间
    ZonedDateTime currentZoned = ZonedDateTime.now();
    System.out.println("当前时区的ZonedDateTime: " + currentZoned); // 示例: 2023-10-27T10:30:45.123+08:00[Asia/Shanghai]
    
    // 获取纽约时区的当前时间
    ZoneId newYorkZone = ZoneId.of("America/New_York");
    ZonedDateTime newYorkTime = ZonedDateTime.now(newYorkZone);
    System.out.println("纽约时区的ZonedDateTime: " + newYorkTime);
    
    // 将当前时区的ZonedDateTime转换为纽约时区
    ZonedDateTime convertedToNewYork = currentZoned.withZoneSameInstant(newYorkZone);
    System.out.println("当前时间在纽约是: " + convertedToNewYork);
    
    // 将LocalDateTime转换为ZonedDateTime
    LocalDateTime localDt = LocalDateTime.of(2023, 10, 27, 10, 0);
    ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
    ZonedDateTime shanghaiZoned = localDt.atZone(shanghaiZone);
    System.out.println("上海的10点: " + shanghaiZoned);
    
    // 将ZonedDateTime转换为Instant
    Instant instantFromZoned = shanghaiZoned.toInstant();
    System.out.println("对应的Instant: " + instantFromZoned);

时区转换的思考:

这里有个小坑,也是很多初学者容易混淆的地方:LocalDateTime 是不带时区的,它表示的是一个“本地”的日期时间。比如“2023年10月27日10点”这个表达,在上海和在纽约,指的

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

846

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

745

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

741

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

420

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

447

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16947

2023.08.03

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

58

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.5万人学习

Java 教程
Java 教程

共578课时 | 50.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号