跳到主要内容Java 时间类(中):JDK8 全新时间 API 详细教程 | 极客日志Javajava
Java 时间类(中):JDK8 全新时间 API 详细教程
JDK8 时间 API 基于 ISO 8601 标准重构,提供不可变对象和线程安全特性。核心类包括 ZoneId 处理时区、Instant 表示 UTC 时间戳、ZonedDateTime 包含完整时区信息。日期时间操作推荐使用 LocalDate、LocalTime 和 LocalDateTime,分别对应年月日、时分秒及组合场景。格式化解析采用 DateTimeFormatter 替代 SimpleDateFormat 解决线程安全问题。时间间隔计算支持 Period(年月日)、Duration(时分秒)及 ChronoUnit(任意单位)。相比 JDK7,新 API 方法语义化强,符合直觉,是日常开发首选。
Java 时间类(中):JDK8 全新时间 API 详细教程
线程安全、设计优雅、日常开发必用,建议和上篇 JDK7 时间类对照学习~
🧠 上篇知识回顾
- JDK7 时间类核心:
Date(表示时间) + SimpleDateFormat(格式化解析) + Calendar(日历操作)
- 痛点:线程不安全、设计混乱(月份从 0 开始)、API 不够直观
- 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. 代码示例
import java.time.ZoneId;
import java.util.Set;
public class ZoneIdDemo {
public static void main(String[] args) {
ZoneId defaultZone = ZoneId.systemDefault();
System.out.println("系统默认时区:" + defaultZone);
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
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. 代码示例
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class InstantDemo {
public static void main(String[] args) {
Instant nowInstant = Instant.now();
System.out.println("当前 UTC 时间戳:" + nowInstant);
Instant zeroInstant = Instant.ofEpochMilli(0L);
System.out.println("0 毫秒对应的时间戳:" + zeroInstant);
ZonedDateTime shanghaiTime = nowInstant.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("上海时区的当前时间:" + shanghaiTime);
Instant oneSecond = Instant.ofEpochSecond(1L);
boolean isBefore = zeroInstant.isBefore(oneSecond);
boolean isAfter = zeroInstant.isAfter(oneSecond);
System.out.println("0 秒是否在 1 秒之前:" + isBefore);
Instant addInstant = zeroInstant.plusSeconds(3600);
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. 代码示例
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeDemo {
public static void main(String[] args) {
ZonedDateTime nowZoned = ZonedDateTime.now();
System.out.println("当前带时区时间:" + nowZoned);
ZonedDateTime customTime = ZonedDateTime.of(2026, 10, 1,
11, 11, 11, 0,
ZoneId.of("Asia/Shanghai")
);
System.out.println("指定的上海时间:" + customTime);
Instant instant = Instant.ofEpochMilli(0L);
ZonedDateTime zeroZoned = ZonedDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
System.out.println("0 毫秒对应的上海时间:" + zeroZoned);
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. 代码示例
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
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 代码示例(最常用)
import java.time.LocalDate;
import java.time.DayOfWeek;
public class LocalDateDemo {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("今天的日期:" + today);
LocalDate customDate = LocalDate.of(2026, 1, 1);
System.out.println("指定日期:" + customDate);
int year = customDate.getYear();
int month = customDate.getMonthValue();
int day = customDate.getDayOfMonth();
DayOfWeek week = customDate.getDayOfWeek();
System.out.println(year + "年" + month + "月" + day + "日 星期" + week.getValue());
boolean isBefore = today.isBefore(customDate);
System.out.println("今天是否在 2026-01-01 之前:" + isBefore);
LocalDate modifyDate = customDate.withYear(2025);
LocalDate addDate = modifyDate.plusMonths(3);
System.out.println("修改并加 3 个月后的日期:" + addDate);
}
}
4. 类之间的相互转换
LocalDateTime 可以拆分为 LocalDate 和 LocalTime:
import java.time.LocalDateTime;
public class LocalConvertDemo {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
LocalDate date = now.toLocalDate();
LocalTime time = now.toLocalTime();
System.out.println("拆分出的日期:" + date);
System.out.println("拆分出的时间:" + time);
}
}
七、时间间隔计算:Period / Duration / ChronoUnit ⏳
日常开发中经常需要计算两个时间的间隔,JDK8 提供了三个工具类,满足不同粒度的需求。
1. Period —— 按'年、月、日'计算日期间隔
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 —— 按'时、分、秒、毫秒'计算时间间隔
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 —— 支持任意时间单位的间隔计算(最灵活)
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);
}
}
📌 本篇知识回顾
- JDK8 时间类均为不可变对象,线程安全,修改/增减时间会返回新对象;
- 高频核心类:LocalDate(年月日)、LocalTime(时分秒)、LocalDateTime(年月日时分秒);
- 格式化工具:DateTimeFormatter 替代 SimpleDateFormat,解决线程安全问题;
- 时间间隔计算:Period(年月日)、Duration(时分秒)、ChronoUnit(任意单位)。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online