Java 时间类(中):JDK8 全新时间 API 详细教程

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyXJAVA游戏规划程序人生

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

  • [Java 时间类(中):JDK8 全新时间 API 详细教程 🕘](#Java 时间类(中):JDK8 全新时间 API 详细教程 🕘)
    • [📝 文章摘要](#📝 文章摘要)
    • [🧠 上篇知识回顾](#🧠 上篇知识回顾)
    • [一、JDK8 时间类整体架构 🏛](#一、JDK8 时间类整体架构 🏛)
    • [二、ZoneId 时区类 🌍](#二、ZoneId 时区类 🌍)
      • [1. 核心作用](#1. 核心作用)
      • [2. 常用方法](#2. 常用方法)
      • [3. 代码示例](#3. 代码示例)
    • [三、Instant 时间戳类 ⚡](#三、Instant 时间戳类 ⚡)
      • [1. 核心作用](#1. 核心作用)
      • [2. 常用方法](#2. 常用方法)
      • [3. 代码示例](#3. 代码示例)
    • [四、ZonedDateTime 带时区时间 🕒](#四、ZonedDateTime 带时区时间 🕒)
      • [1. 核心特点](#1. 核心特点)
      • [2. 常用方法](#2. 常用方法)
      • [3. 代码示例](#3. 代码示例)
    • [五、DateTimeFormatter 格式化解析 ✨](#五、DateTimeFormatter 格式化解析 ✨)
      • [1. 核心优势](#1. 核心优势)
      • [2. 常用方法](#2. 常用方法)
      • [3. 格式规则(与 SimpleDateFormat 一致)](#3. 格式规则(与 SimpleDateFormat 一致))
      • [4. 代码示例](#4. 代码示例)
    • [六、LocalDate / LocalTime / LocalDateTime 🌟](#六、LocalDate / LocalTime / LocalDateTime 🌟)
      • [1. 功能分工](#1. 功能分工)
      • [2. 通用核心方法](#2. 通用核心方法)
      • [3. LocalDate 代码示例(最常用)](#3. LocalDate 代码示例(最常用))
      • [4. 类之间的相互转换](#4. 类之间的相互转换)
    • [七、时间间隔计算:Period / Duration / ChronoUnit ⏳](#七、时间间隔计算:Period / Duration / ChronoUnit ⏳)
      • [1. Period ------ 按"年、月、日"计算日期间隔](#1. Period —— 按“年、月、日”计算日期间隔)
      • [2. Duration ------ 按"时、分、秒、毫秒"计算时间间隔](#2. Duration —— 按“时、分、秒、毫秒”计算时间间隔)
      • [3. ChronoUnit ------ 支持任意时间单位的间隔计算(最灵活)](#3. ChronoUnit —— 支持任意时间单位的间隔计算(最灵活))
    • [📌 本篇知识回顾](#📌 本篇知识回顾)
    • [✍️ 写在最后](#✍️ 写在最后)

Java 时间类(中):JDK8 全新时间 API 详细教程 🕘

线程安全、设计优雅、日常开发必用,建议和上篇 JDK7 时间类对照学习~

📝 文章摘要

  • 阅读时长:10 分钟
  • 适合人群
    1. 已经学完 JDK7 时间类,想升级 JDK8 写法的同学
    2. Java 初/中级开发者 → 重点看:LocalDateTime、DateTimeFormatter、时间计算
    3. 面试/工作党 → 重点看:不可变对象、线程安全、时间间隔工具
  • 本文内容:系统讲解 JDK8 全新时间类,包括时区、时间戳、日期时间、格式化、时间间隔,覆盖日常开发高频用法。

🧠 上篇知识回顾

  1. JDK7 时间类核心:Date(表示时间) + SimpleDateFormat(格式化解析) + Calendar(日历操作)
  2. 痛点:线程不安全、设计混乱(月份从 0 开始)、API 不够直观
  3. JDK8 新时间类优势:
    • 不可变对象,天然线程安全
    • 方法命名语义化,见名知意
    • 年月日常规从 1 开始,符合直觉
    • 按功能拆分(日期/时间/日期+时间),职责清晰

一、JDK8 时间类整体架构 🏛

JDK8 基于 ISO 8601 标准重构了时间 API,把时间功能拆解得清晰易懂:

类别 核心类 作用
时区 ZoneId 表示时区(如 Asia/Shanghai)
时间戳 Instant 表示 UTC 标准时间戳
带时区时间 ZonedDateTime 带时区的完整时间
纯日期 LocalDate 仅包含年月日
纯时间 LocalTime 仅包含时分秒纳秒
日期+时间 LocalDateTime 包含年月日时分秒
格式化解析 DateTimeFormatter 替代 SimpleDateFormat
时间间隔 Period/Duration/ChronoUnit 计算两个时间的间隔

二、ZoneId 时区类 🌍

1. 核心作用

表示全球各个时区,解决不同地区时间偏移的问题,是处理跨时区时间的基础。

2. 常用方法

方法名 说明
ZoneId.systemDefault() 获取系统默认时区
ZoneId.of(String zoneId) 根据时区名获取指定时区
ZoneId.getAvailableZoneIds() 获取 Java 支持的所有时区

3. 代码示例

java 复制代码
import java.time.ZoneId;
import java.util.Set;

public class ZoneIdDemo {
    public static void main(String[] args) {
        // 1. 获取系统默认时区(国内通常是 Asia/Shanghai)
        ZoneId defaultZone = ZoneId.systemDefault();
        System.out.println("系统默认时区:" + defaultZone);

        // 2. 获取指定时区
        ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
        ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");

        // 3. 查看所有支持的时区(约600个)
        Set<String> allZones = ZoneId.getAvailableZoneIds();
        System.out.println("Java支持的时区总数:" + allZones.size());
    }
}

三、Instant 时间戳类 ⚡

1. 核心作用

表示时间线上的一个瞬时点,对应 JDK7 的 Date 类,底层存储的是从 1970-01-01 00:00:00 UTC 开始的毫秒/纳秒数。

2. 常用方法

方法名 说明
Instant.now() 获取当前 UTC 时间戳
Instant.ofEpochMilli(long) 根据毫秒值构建 Instant
Instant.ofEpochSecond(long) 根据秒值构建 Instant
atZone(ZoneId zone) 为时间戳绑定指定时区
isBefore(Instant other) 判断当前时间是否在参数时间之前
isAfter(Instant other) 判断当前时间是否在参数时间之后
plusSeconds(long) / minusSeconds(long) 增加/减少指定秒数

3. 代码示例

java 复制代码
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class InstantDemo {
    public static void main(String[] args) {
        // 1. 获取当前 UTC 时间戳
        Instant nowInstant = Instant.now();
        System.out.println("当前UTC时间戳:" + nowInstant);

        // 2. 根据毫秒值构建(0毫秒对应1970-01-01 00:00:00 UTC)
        Instant zeroInstant = Instant.ofEpochMilli(0L);
        System.out.println("0毫秒对应的时间戳:" + zeroInstant);

        // 3. 为时间戳绑定时区(转换为带时区的时间)
        ZonedDateTime shanghaiTime = nowInstant.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println("上海时区的当前时间:" + shanghaiTime);

        // 4. 时间比较
        Instant oneSecond = Instant.ofEpochSecond(1L);
        boolean isBefore = zeroInstant.isBefore(oneSecond); // true
        boolean isAfter = zeroInstant.isAfter(oneSecond);  // false
        System.out.println("0秒是否在1秒之前:" + isBefore);

        // 5. 时间加减
        Instant addInstant = zeroInstant.plusSeconds(3600); // 加1小时
        System.out.println("加1小时后的时间:" + addInstant);
    }
}

四、ZonedDateTime 带时区时间 🕒

1. 核心特点

  • 包含完整的"日期+时间+时区"信息,是 Instant + ZoneId 的组合体
  • 不可变对象:修改时间会返回新的对象,原对象保持不变
  • 方法语义化,操作更直观

2. 常用方法

方法类型 核心方法 说明
获取对象 ZonedDateTime.now() 获取当前带时区时间
ZonedDateTime.of(年,月,日,时,分,秒,纳秒,时区) 指定时间创建对象
ZonedDateTime.ofInstant(Instant, ZoneId) 通过时间戳+时区创建
修改时间 withYear(int) / withMonth(int) 修改年/月(返回新对象)
增减时间 plusYears(long) / minusMonths(long) 增加/减少年/月(返回新对象)

3. 代码示例

java 复制代码
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        // 1. 获取当前带时区时间
        ZonedDateTime nowZoned = ZonedDateTime.now();
        System.out.println("当前带时区时间:" + nowZoned);

        // 2. 指定时间创建
        ZonedDateTime customTime = ZonedDateTime.of(
            2026, 10, 1,    // 年月日
            11, 11, 11, 0,  // 时分秒纳秒
            ZoneId.of("Asia/Shanghai") // 时区
        );
        System.out.println("指定的上海时间:" + customTime);

        // 3. 通过Instant+时区创建
        Instant instant = Instant.ofEpochMilli(0L);
        ZonedDateTime zeroZoned = ZonedDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
        System.out.println("0毫秒对应的上海时间:" + zeroZoned);

        // 4. 修改时间(返回新对象,原对象不变)
        ZonedDateTime modifyYear = customTime.withYear(2025);
        ZonedDateTime minusMonth = modifyYear.minusMonths(1);
        System.out.println("修改并减1个月后的时间:" + minusMonth);
    }
}

五、DateTimeFormatter 格式化解析 ✨

1. 核心优势

替代 JDK7 的 SimpleDateFormat,解决了线程不安全的问题,是 JDK8 推荐的时间格式化工具。

2. 常用方法

方法名 说明
DateTimeFormatter.ofPattern(String) 根据指定格式创建格式化器
format(TemporalAccessor temporal) 将时间对象格式化为字符串

3. 格式规则(与 SimpleDateFormat 一致)

常用格式模板:

  • yyyy-MM-dd:仅日期(如 2026-02-20)
  • HH:mm:ss:仅时间(如 15:30:45)
  • yyyy-MM-dd HH:mm:ss:日期+时间(如 2026-02-20 15:30:45)

4. 代码示例

java 复制代码
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterDemo {
    public static void main(String[] args) {
        // 1. 获取带时区的当前时间
        ZonedDateTime now = ZonedDateTime.now();

        // 2. 创建格式化器(指定格式)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 3. 格式化时间(时间对象 → 字符串)
        String formatTime = formatter.format(now);
        System.out.println("格式化后的时间:" + formatTime);
    }
}

六、LocalDate / LocalTime / LocalDateTime 🌟

这三个类是 JDK8 时间 API 中日常开发使用频率最高的,按功能拆分,满足不同场景需求。

1. 功能分工

类名 包含信息 适用场景
LocalDate 年、月、日 生日、签到日期、订单日期
LocalTime 时、分、秒、纳秒 上下班打卡时间、活动开始时间
LocalDateTime 年、月、日、时、分、秒 日志记录、接口请求时间、数据创建时间

2. 通用核心方法

方法类型 核心方法 说明
获取对象 XXX.now() 获取当前时间/日期
XXX.of(...) 指定时间/日期创建对象
获取字段 getYear() / getMonthValue() 获取年/月(月份1-12)
getDayOfMonth() / getHour() 获取日/时
修改时间 withYear(int) / withDayOfMonth(int) 修改年/日
增减时间 plusDays(long) / minusHours(long) 增加/减少天/小时
时间比较 isBefore(XXX) / isAfter(XXX) 判断时间先后

3. LocalDate 代码示例(最常用)

java 复制代码
import java.time.LocalDate;
import java.time.DayOfWeek;

public class LocalDateDemo {
    public static void main(String[] args) {
        // 1. 获取当前日期
        LocalDate today = LocalDate.now();
        System.out.println("今天的日期:" + today);

        // 2. 指定日期创建
        LocalDate customDate = LocalDate.of(2026, 1, 1);
        System.out.println("指定日期:" + customDate);

        // 3. 获取日期字段
        int year = customDate.getYear(); // 2026
        int month = customDate.getMonthValue(); // 1(1-12,无需+1)
        int day = customDate.getDayOfMonth(); // 1
        DayOfWeek week = customDate.getDayOfWeek(); // 星期几
        System.out.println(year + "年" + month + "月" + day + "日 星期" + week.getValue());

        // 4. 时间比较
        boolean isBefore = today.isBefore(customDate);
        System.out.println("今天是否在2026-01-01之前:" + isBefore);

        // 5. 时间修改与增减
        LocalDate modifyDate = customDate.withYear(2025); // 修改年
        LocalDate addDate = modifyDate.plusMonths(3); // 加3个月
        System.out.println("修改并加3个月后的日期:" + addDate);
    }
}

4. 类之间的相互转换

LocalDateTime 可以拆分为 LocalDate 和 LocalTime:

java 复制代码
import java.time.LocalDateTime;

public class LocalConvertDemo {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        
        // LocalDateTime → LocalDate
        LocalDate date = now.toLocalDate();
        // LocalDateTime → LocalTime
        LocalTime time = now.toLocalTime();
        
        System.out.println("拆分出的日期:" + date);
        System.out.println("拆分出的时间:" + time);
    }
}

七、时间间隔计算:Period / Duration / ChronoUnit ⏳

日常开发中经常需要计算两个时间的间隔,JDK8 提供了三个工具类,满足不同粒度的需求。

1. Period ------ 按"年、月、日"计算日期间隔

java 复制代码
import java.time.LocalDate;
import java.time.Period;

public class PeriodDemo {
    public static void main(String[] args) {
        // 起始日期
        LocalDate start = LocalDate.of(2020, 1, 1);
        // 结束日期
        LocalDate end = LocalDate.now();
        
        // 计算间隔
        Period period = Period.between(start, end);
        System.out.println("年差:" + period.getYears());
        System.out.println("月差:" + period.getMonths());
        System.out.println("日差:" + period.getDays());
        System.out.println("总月数:" + period.toTotalMonths());
    }
}

2. Duration ------ 按"时、分、秒、毫秒"计算时间间隔

java 复制代码
import java.time.LocalDateTime;
import java.time.Duration;

public class DurationDemo {
    public static void main(String[] args) {
        // 起始时间
        LocalDateTime start = LocalDateTime.of(2020, 1, 1, 0, 0);
        // 结束时间
        LocalDateTime end = LocalDateTime.now();
        
        // 计算间隔
        Duration duration = Duration.between(start, end);
        System.out.println("天差:" + duration.toDays());
        System.out.println("小时差:" + duration.toHours());
        System.out.println("分钟差:" + duration.toMinutes());
        System.out.println("毫秒差:" + duration.toMillis());
    }
}

3. ChronoUnit ------ 支持任意时间单位的间隔计算(最灵活)

java 复制代码
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class ChronoUnitDemo {
    public static void main(String[] args) {
        // 起始时间
        LocalDateTime start = LocalDateTime.of(2007, 8, 13, 14, 0);
        // 结束时间
        LocalDateTime end = LocalDateTime.now();
        
        // 计算不同单位的间隔
        long years = ChronoUnit.YEARS.between(start, end);
        long months = ChronoUnit.MONTHS.between(start, end);
        long days = ChronoUnit.DAYS.between(start, end);
        long hours = ChronoUnit.HOURS.between(start, end);
        
        System.out.println("年差:" + years);
        System.out.println("月差:" + months);
        System.out.println("天差:" + days);
        System.out.println("小时差:" + hours);
    }
}

📌 本篇知识回顾

  1. JDK8 时间类均为不可变对象,线程安全,修改/增减时间会返回新对象;
  2. 高频核心类:LocalDate(年月日)、LocalTime(时分秒)、LocalDateTime(年月日时分秒);
  3. 格式化工具:DateTimeFormatter 替代 SimpleDateFormat,解决线程安全问题;
  4. 时间间隔计算:Period(年月日)、Duration(时分秒)、ChronoUnit(任意单位)。

✍️ 写在最后

本篇我们系统学习了 JDK8 全新的时间 API,相比 JDK7 的传统时间类,它更优雅、更安全、更易用,是日常开发的首选。

下一篇我们将学习 Java 包装类相关知识:包括基本数据类型对应的包装类、自动装箱拆箱、常量池、字符串转基本类型等,这是 Java 基础的重要组成部分,也是面试高频考点!

如果你觉得本文对你有帮助,欢迎点赞 👍、收藏 💾、评论 💬,后续持续更新 Java 基础系列内容~

相关推荐
时艰.1 小时前
电商项目支付宝支付实战
java·服务器·网络
kong79069281 小时前
Python核心语法-Matplotlib简介
开发语言·python·matplotlib
mjhcsp2 小时前
C++数位 DP解析
开发语言·c++·动态规划
Coder_Boy_2 小时前
Java高级_资深_架构岗 核心知识点——高并发模块(底层+实践+最佳实践)
java·开发语言·人工智能·spring boot·分布式·微服务·架构
小龙报2 小时前
【算法通关指南:数据结构与算法篇】二叉树相关算法题:1.二叉树深度 2.求先序排列
c语言·开发语言·数据结构·c++·算法·贪心算法·动态规划
yy.y--2 小时前
Java线程实现浏览器实时时钟
java·linux·开发语言·前端·python
笨蛋不要掉眼泪2 小时前
Sentinel 流控规则详解:三种模式与三种效果实战指南
java·jvm·数据库·后端·sentinel
echoVic2 小时前
给 Agent Skill 装上「黑匣子」:STOP 可观测性协议设计与实现
java·javascript
阿乐艾官2 小时前
【K8s思维导图及单节点容器启动流程】
java·容器·kubernetes