一、核心定位:为什么它是「第三代」& 终极方案
Java 日期时间 API 发展历经 三代 ,java.time 包(JDK 1.8 推出)是官方最终的最优解,也被称为 JSR 310 规范,彻底解决了前两代 API 的所有痛点:
✅ 三代日期 API 对比(清晰选型)
| 版本 | 核心类 | 定位 | 致命缺陷 | 现状 |
|---|---|---|---|---|
| 第一代 | java.util.Date |
仅表示「时间瞬间」 | 方法大量过时、无计算能力、设计混乱 | 几乎弃用,仅兼容老代码 |
| 第二代 | java.util.Calendar |
日期计算工具 | 月份 0 开始、星期设计反人类、线程不安全、API 繁琐 | 老项目过渡用,新项目禁用 |
| 第三代 | java.time 全家桶 |
一站式日期时间解决方案 | ✅ 无缺陷 | JDK8+ 开发首选,强制推荐 |
✅ 第三代 API 核心优势(碾压前两代)
✅ 线程安全 :所有类都是「不可变对象」,修改会生成新对象,无并发风险✅ 设计优雅 :日期、时间、日期 + 时间、时区彻底分离,语义清晰,见名知意✅ 无坑点 :月份从1开始、星期从1开始,完全符合人类认知,告别手动修正✅ 功能强大 :原生支持日期计算、格式化、解析、时区转换、闰年判断,无需工具类✅ API 简洁:支持链式调用,一行代码完成复杂日期操作,开发效率翻倍
二、核心前置知识:java.time 包核心类划分(必记)
java.time 包对 日期、时间、时区、格式化 做了极致拆分,各司其职,这是它优雅的核心。核心类按功能分为 4 大类,必须熟记用途,杜绝用错类!
✅ 1. 「无时区」核心类(开发 90% 场景使用)
💡 特点:不关心时区,仅表示「本地」的日期 / 时间,是日常开发最常用的三类核心类。
✔️ LocalDate → 仅存「日期」(年 + 月 + 日)
- 用途:只处理年月日相关业务,比如「生日、订单日期、会员到期日」
- 示例:
2025-12-29,不含时分秒
✔️ LocalTime → 仅存「时间」(时 + 分 + 秒 + 毫秒)
- 用途:只处理时分秒相关业务,比如「闹钟时间、营业时间、接口响应耗时」
- 示例:
18:30:59.123,不含年月日
✔️ LocalDateTime → 日期 + 时间(年 + 月 + 日 + 时 + 分 + 秒 + 毫秒)
- 用途:处理完整的日期 + 时间业务,比如「订单创建时间、日志时间、任务执行时间」
- 示例:
2025-12-29 18:30:59.123→ ✅ 开发最高频使用!
✅ 2. 「有时区」核心类(跨时区业务专用)
💡 特点:绑定时区 / UTC 时间,精准表示「全球唯一的时间瞬间」,用于国际化、跨时区系统。
✔️ Instant → UTC 时间戳(对应 Date)
- 用途:表示 UTC 标准时间(1970-01-01 00:00:00 到此刻的毫秒数),等价于 Date 类,常用于「时间戳存储、跨系统时间传输」
- 示例:
2025-12-29T10:30:59.123Z(Z 代表 UTC 时区)
✔️ ZonedDateTime → 带时区的完整时间
- 用途:表示「指定时区」的日期 + 时间,比如「纽约时间、东京时间」,用于国际化业务
- 示例:
2025-12-29T18:30:59.123+08:00[Asia/Shanghai](东八区)
✅ 3. 时间间隔类(计算时间差专用)
✔️ Duration → 计算「时分秒」间隔(秒 / 毫秒级)
- 用途:两个时间 / 日期时间的间隔,精确到秒、毫秒,比如「接口耗时、任务执行时长」
✔️ Period → 计算「年月日」间隔(天 / 月 / 年级)
- 用途:两个日期的间隔,精确到年、月、日,比如「年龄计算、会员剩余天数」
✅ 4. 格式化 & 解析类(专属工具)
✔️ DateTimeFormatter → 格式化 + 解析工具
- 用途:替代第一代的
SimpleDateFormat,完成「日期对象 ↔ 字符串」的转换 - 核心优势:线程安全、支持自定义格式、内置标准格式,无并发风险 → ✅ 开发首选!
三、核心类实战(按使用频率排序,全示例 + 详解)
✅ 一、高频王者:LocalDateTime(日期 + 时间,90% 场景必用)
✔️ 1. 创建 LocalDateTime 对象(4 种常用方式)
import java.time.LocalDateTime;
public class LocalDateTimeDemo {
public static void main(String[] args) {
// 方式1:获取【当前系统】的日期+时间(最常用)
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now); // 输出:2025-12-29T18:45:30.123
// 方式2:手动指定【年月日时分秒】创建(精准定制,常用)
LocalDateTime target = LocalDateTime.of(2025, 12, 25, 20, 0, 0);
System.out.println("指定时间:" + target); // 输出:2025-12-25T20:00
// 方式3:LocalDate + LocalTime 组合创建
LocalDate date = LocalDate.of(2025, 12, 29);
LocalTime time = LocalTime.of(18, 50);
LocalDateTime combine = LocalDateTime.of(date, time);
System.out.println("组合创建:" + combine); // 输出:2025-12-29T18:50
// 方式4:字符串解析创建(配合格式化器,后文详解)
}
}
✔️ 2. 获取时间字段(年 / 月 / 日 / 时 / 分 / 秒,无坑!)
💡 核心亮点:月份返回
1-12、星期返回1-7(周一 = 1,周日 = 7),完全符合人类认知,无需任何修正!
LocalDateTime now = LocalDateTime.now();
// 获取核心字段
int year = now.getYear(); // 年份:2025
int month = now.getMonthValue(); // 月份:12(直接返回1-12,无坑)
int day = now.getDayOfMonth(); // 日期:29
int hour = now.getHour(); // 小时:18
int minute = now.getMinute(); // 分钟:45
int second = now.getSecond(); // 秒:30
int nano = now.getNano(); // 纳秒(1毫秒=100万纳秒)
int week = now.getDayOfWeek().getValue(); // 星期:1(周一)~7(周日)
System.out.printf("当前:%d年%d月%d日 %02d:%02d:%02d 星期%d%n",
year, month, day, hour, minute, second, week);
✔️ 3. 日期计算(增减操作,核心能力,支持链式调用)
💡 核心亮点:不可变对象,所有增减操作都会返回「新对象」,原对象不变;支持任意字段的增减,API 语义化极强。
LocalDateTime now = LocalDateTime.now();
System.out.println("原始时间:" + now);
// ========== 字段增减(推荐:链式调用,一行完成) ==========
LocalDateTime newTime = now
.plusYears(1) // 加1年
.minusMonths(2) // 减2个月
.plusDays(7) // 加7天
.minusHours(3) // 减3小时
.plusMinutes(30); // 加30分钟
System.out.println("计算后时间:" + newTime);
// ========== 单独增减(按需使用) ==========
LocalDateTime nextWeek = now.plusWeeks(1); // 加1周
LocalDateTime lastSecond = now.minusSeconds(1); // 减1秒
✔️ 4. 时间比较(isAfter/isBefore/isEqual,语义清晰)
LocalDateTime time1 = LocalDateTime.of(2025,12,25,20,0);
LocalDateTime time2 = LocalDateTime.of(2025,12,29,18,0);
boolean isAfter = time2.isAfter(time1); // true:time2晚于time1
boolean isBefore = time2.isBefore(time1); // false:time2早于time1
boolean isEqual = time2.isEqual(time1); // false:时间相等
System.out.println("time2是否晚于time1:" + isAfter);
✅ 二、高频次类:LocalDate(仅日期)+ LocalTime(仅时间)
二者 API 与 LocalDateTime 高度一致,仅操作维度不同(一个只有日期、一个只有时间),复用性极强,快速掌握核心用法即可:
✔️ LocalDate 核心用法(仅年月日)
import java.time.LocalDate;
public class LocalDateDemo {
public static void main(String[] args) {
// 1. 创建:当前日期 / 指定日期
LocalDate nowDate = LocalDate.now(); // 2025-12-29
LocalDate targetDate = LocalDate.of(2025, 12, 25); // 2025-12-25
// 2. 获取字段
int year = nowDate.getYear();
int month = nowDate.getMonthValue();
int day = nowDate.getDayOfMonth();
int week = nowDate.getDayOfWeek().getValue();
// 3. 日期计算
LocalDate nextDay = nowDate.plusDays(1); // 次日
LocalDate lastMonth = nowDate.minusMonths(1); // 上月今日
// 4. 常用判断
boolean isLeapYear = nowDate.isLeapYear(); // 判断是否闰年 ✅ 原生支持!
boolean isBefore = targetDate.isBefore(nowDate); // true
}
}
✔️ LocalTime 核心用法(仅时分秒)
import java.time.LocalTime;
public class LocalTimeDemo {
public static void main(String[] args) {
// 1. 创建:当前时间 / 指定时间
LocalTime nowTime = LocalTime.now(); // 18:55:30.123
LocalTime targetTime = LocalTime.of(20, 0, 0); // 20:00:00
// 2. 获取字段
int hour = nowTime.getHour();
int minute = nowTime.getMinute();
int second = nowTime.getSecond();
// 3. 时间计算
LocalTime after2Hour = nowTime.plusHours(2); // 加2小时
LocalTime before30Min = nowTime.minusMinutes(30); // 减30分钟
}
}
✅ 三、核心工具:DateTimeFormatter(格式化 & 解析,线程安全)
💡 替代第一代的
SimpleDateFormat,解决其 线程不安全 的致命缺陷,是java.time包的专属格式化工具,开发强制使用 !核心能力:日期对象 → 自定义格式字符串(格式化)、字符串 → 日期对象(解析)
✔️ 格式模式符(与 SimpleDateFormat 通用,必记)
yyyy(4位年)、MM(2位月)、dd(2位日)、HH(24小时)、hh(12小时)、mm(分)、ss(秒)、SSS(毫秒)
✔️ 完整示例:格式化 + 解析
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
// 1. 定义格式化器(两种方式,推荐方式2:自定义格式)
// 方式1:使用内置标准格式(无需自定义)
DateTimeFormatter fmt1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 方式2:自定义格式(开发最常用,模式符大小写敏感)
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
// ========== 操作1:格式化(日期对象 → 字符串) ==========
String str1 = fmt1.format(now); // 标准格式:2025-12-29T19:00:00.123
String str2 = fmt2.format(now); // 自定义格式:2025-12-29 19:00:00 123
System.out.println("标准格式:" + str1);
System.out.println("自定义格式:" + str2);
// ========== 操作2:解析(字符串 → 日期对象) ==========
String timeStr = "2025-12-25 20:00:00 000";
try {
// 注意:字符串格式必须与格式化器模式完全匹配,否则抛DateTimeParseException
LocalDateTime parseTime = LocalDateTime.parse(timeStr, fmt2);
System.out.println("解析后的时间:" + parseTime);
} catch (DateTimeParseException e) {
e.printStackTrace();
}
}
}
✅ 四、时间差计算:Duration(时分秒)+ Period(年月日)
💡 前两代 API 计算时间差需要手动通过「时间戳相减」实现,极其繁琐;第三代 API 原生提供两个工具类,精准计算时间差,无需手动处理。
✔️ Duration → 计算「时分秒 / 毫秒」差(时间 / 日期时间)
import java.time.Duration;
import java.time.LocalDateTime;
public class DurationDemo {
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2025,12,25,20,0,0);
LocalDateTime end = LocalDateTime.of(2025,12,29,18,30,59);
// 计算两个时间的间隔
Duration duration = Duration.between(start, end);
// 获取间隔的具体值
long days = duration.toDays(); // 总天数
long hours = duration.toHours(); // 总小时数
long minutes = duration.toMinutes(); // 总分钟数
long seconds = duration.getSeconds(); // 总秒数
long millis = duration.toMillis(); // 总毫秒数
System.out.printf("间隔:%d天%d小时%n", days, duration.toHoursPart());
}
}
✔️ Period → 计算「年月日」差(仅日期)
import java.time.LocalDate;
import java.time.Period;
public class PeriodDemo {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2000, 1, 1);
LocalDate end = LocalDate.of(2025, 12, 29);
// 计算两个日期的间隔
Period period = Period.between(start, end);
// 获取间隔的年、月、日(单独值,非总数量)
int years = period.getYears(); // 25年
int months = period.getMonths(); // 11个月
int days = period.getDays(); // 28天
System.out.printf("间隔:%d年%d月%d日%n", years, months, days);
}
}
✅ 五、跨代兼容:与 Date/Calendar 互转(老项目必备)
实际开发中难免遇到老代码(Date/Calendar),java.time 提供了无缝互转方案,核心桥梁是 Instant 类 (对应 Date,均表示时间戳瞬间),配合时区 ZoneId 完成转换。
✔️ 完整互转示例(开发高频)
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
public class ConvertDemo {
public static void main(String[] args) {
ZoneId zoneId = ZoneId.systemDefault(); // 系统默认时区(东八区 Asia/Shanghai)
LocalDateTime ldt = LocalDateTime.of(2025,12,29,19,0,0);
// ========== 1. 第三代 ↔ Date ==========
// LocalDateTime → Date
Instant instant1 = ldt.atZone(zoneId).toInstant();
Date date1 = Date.from(instant1);
// Date → LocalDateTime
Instant instant2 = date1.toInstant();
LocalDateTime ldt2 = LocalDateTime.ofInstant(instant2, zoneId);
// ========== 2. 第三代 ↔ Calendar ==========
// Calendar → LocalDateTime
Calendar cal = Calendar.getInstance();
Date date2 = cal.getTime();
LocalDateTime ldt3 = LocalDateTime.ofInstant(date2.toInstant(), zoneId);
// LocalDateTime → Calendar(间接:先转Date)
Calendar cal2 = Calendar.getInstance();
cal2.setTime(Date.from(ldt.atZone(zoneId).toInstant()));
}
}
三、扩展类:Instant(时间戳)+ ZonedDateTime(带时区)
✅ Instant → 等价于 Date,UTC 时间戳
import java.time.Instant;
public class InstantDemo {
public static void main(String[] args) {
// 1. 创建:当前UTC时间、指定时间戳
Instant now = Instant.now(); // UTC当前时间
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis()); // 通过毫秒数创建
// 2. 转时间戳(毫秒)
long timestamp = now.toEpochMilli(); // 等价于 new Date().getTime()
// 3. 与Date互转(直接支持)
Date date = Date.from(now);
Instant instant2 = date.toInstant();
}
}
✅ ZonedDateTime → 带时区的完整时间(国际化专用)
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeDemo {
public static void main(String[] args) {
// 1. 创建:系统时区、指定时区(纽约/东京/伦敦)
ZonedDateTime now = ZonedDateTime.now(); // 系统默认时区(东八区)
ZonedDateTime nyTime = ZonedDateTime.now(ZoneId.of("America/New_York")); // 纽约时间
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo")); // 东京时间
System.out.println("上海时间:" + now);
System.out.println("纽约时间:" + nyTime);
}
}
四、核心总结 & 开发强制规范(必遵循)
✅ 核心知识点总结
- Java 第三代日期 API 是
java.time包(JDK8+),又称 JSR310,是终极最优解; - 核心类按用途拆分:
LocalDateTime(高频)、LocalDate/LocalTime(常用)、DateTimeFormatter(格式化)、Duration/Period(时间差); - 所有类都是不可变对象,修改会生成新对象,天然线程安全;
- 无坑点:月份 1-12、星期 1-7,符合人类认知,无需修正;
- 支持链式调用 、原生计算 、原生格式化,API 语义化极强。
✅ 开发强制规范(直接套用)
- JDK8+ 新项目 :全程使用 java.time 包,彻底抛弃 Date/Calendar/SimpleDateFormat;
- 日期 + 时间业务 → 用
LocalDateTime;仅日期 → 用LocalDate;仅时间 → 用LocalTime; - 格式化 / 解析 → 强制用
DateTimeFormatter,杜绝SimpleDateFormat; - 时间差计算 → 用
Duration(时分秒)/Period(年月日); - 老项目兼容 → 通过
Instant+ZoneId实现与 Date/Calendar 互转; - 跨时区业务 → 用
ZonedDateTime/Instant。
✅ 一句话口诀(快速记忆)
本地用 Local 三兄弟,格式化用 DateTimeFormatter,时间差用 Duration/Period,时区用 Zoned/Instant
至此,Java 日期时间的所有知识已全部覆盖,掌握第三代 API 即可从容应对所有日期相关开发场景,告别前两代的所有坑点!
