跳到主要内容Java 时间类(中):JDK8 全新时间 API 详解 | 极客日志Javajava
Java 时间类(中):JDK8 全新时间 API 详解
JDK8 时间 API 基于 ISO 8601 标准重构,解决了旧版 Date 和 Calendar 的线程不安全及设计混乱问题。核心改进包括不可变对象、语义化命名及按功能拆分。详细讲解了 ZoneId 时区处理、Instant 时间戳、ZonedDateTime 带时区时间、DateTimeFormatter 格式化解析,以及 LocalDate/LocalTime/LocalDateTime 三大核心类的用法。此外还涵盖了 Period、Duration 和 ChronoUnit 三种时间间隔计算工具,覆盖了日常开发中日期时间处理的高频场景。
PhpPioneer1 浏览 Java 时间类(中):JDK8 全新时间 API 详解
JDK8 引入的时间 API 彻底解决了旧版 Date/Calendar 的线程安全和易用性问题,是日常开发的首选。
JDK8 时间类整体架构
JDK8 基于 ISO 8601 标准重构了时间 API,将功能拆解得更加清晰。相比 JDK7 的 Date + SimpleDateFormat + Calendar,新 API 具备不可变对象、线程安全、语义化命名等核心优势。
| 类别 | 核心类 | 作用 |
|---|
| 时区 | ZoneId | 表示时区(如 Asia/Shanghai) |
| 时间戳 | Instant | 表示 UTC 标准时间戳 |
| 带时区时间 | ZonedDateTime | 带时区的完整时间 |
| 纯日期 | LocalDate | 仅包含年月日 |
| 纯时间 | LocalTime | 仅包含时分秒纳秒 |
| 日期 + 时间 | LocalDateTime | 包含年月日时分秒 |
| 格式化解析 | DateTimeFormatter | 替代 SimpleDateFormat |
| 时间间隔 | Period/Duration/ChronoUnit | 计算两个时间的间隔 |
ZoneId 时区类
处理跨时区业务的基础。它负责管理全球各个时区及其偏移量。
常用方法
ZoneId.systemDefault():获取系统默认时区。
ZoneId.of(String zoneId):根据 ID 获取指定时区。
ZoneId.getAvailableZoneIds():获取 Java 支持的所有时区 ID。
代码示例
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.of();
ZoneId.of();
Set<String> allZones = ZoneId.getAvailableZoneIds();
System.out.println( + allZones.size());
}
}
ZoneId
shanghaiZone
=
"Asia/Shanghai"
ZoneId
tokyoZone
=
"Asia/Tokyo"
"Java 支持的时区总数:"
Instant 时间戳类
Instant 代表时间线上的一个瞬时点,底层存储的是从 1970-01-01 00:00:00 UTC 开始的毫秒或纳秒数。它是所有时间操作的核心基准。
常用方法
Instant.now():获取当前 UTC 时间戳。
Instant.ofEpochMilli(long) / ofEpochSecond(long):根据数值构建。
atZone(ZoneId zone):为时间戳绑定时区。
isBefore / isAfter:比较时间点先后。
代码示例
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);
System.out.println("0 秒是否在 1 秒之前:" + isBefore);
Instant addInstant = zeroInstant.plusSeconds(3600);
System.out.println("加 1 小时后的时间:" + addInstant);
}
}
ZonedDateTime 带时区时间
这是最完整的日期时间表示,包含了'日期 + 时间 + 时区'信息。由于是不可变对象,任何修改操作都会返回新的实例,原对象保持不变。
常用方法
ZonedDateTime.now():获取当前带时区时间。
ZonedDateTime.of(...):通过参数直接创建。
withYear(int) / plusYears(long):修改或增减时间。
代码示例
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 格式化解析
JDK8 推荐使用 DateTimeFormatter 替代 SimpleDateFormat。前者是线程安全的,且设计更灵活。
格式规则
yyyy-MM-dd:仅日期
HH:mm:ss:仅时间
yyyy-MM-dd HH:mm:ss:日期 + 时间
代码示例
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
这三个类是日常开发中使用频率最高的,按功能拆分,职责单一。
功能分工
| 类名 | 包含信息 | 适用场景 |
|---|
| LocalDate | 年、月、日 | 生日、订单日期 |
| LocalTime | 时、分、秒、纳秒 | 打卡时间、活动开始 |
| LocalDateTime | 年、月、日、时、分、秒 | 日志记录、接口请求时间 |
通用核心方法
XXX.now():获取当前时间。
getYear() / getMonthValue():获取字段(月份 1-12)。
plusDays(long) / minusHours(long):增减时间。
isBefore / isAfter:比较时间。
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);
}
}
类之间的相互转换
LocalDateTime 可以拆分为日期和时间部分:
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 提供了三个工具类,分别针对不同粒度。
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());
}
}
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());
}
}
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