Java 日期时间处理全套教程:旧 Date 避坑 + JDK8 全新 LocalDateTime 实战
·
一、前言
Java 处理日期分为两套 API:
- JDK1.0 老旧 API:
Date、SimpleDateFormat,存在线程不安全、设计不合理、修改时间会改变原对象等缺陷,生产环境尽量少用。 - JDK8 全新时间 API:
LocalDate/LocalTime/LocalDateTime、DateTimeFormatter、ChronoUnit,不可变对象、线程安全、API 清晰,是企业开发标准推荐方案。
本文完整梳理两套 API 的构造、格式化、时间增减、间隔计算,附完整可运行代码与开发避坑点。
二、JDK8 之前旧时间 API(Date + SimpleDateFormat)
1. Date 类
构造方法
| 构造器 | 功能说明 |
|---|---|
new Date() |
创建对象,代表系统当前时间戳 |
new Date(long time) |
根据毫秒时间戳,转换为日期对象 |
核心常用方法
long getTime():获取 1970-01-01 至今的总毫秒数void setTime(long time):修改 Date 对象的时间戳
// 获取当前系统时间
Date d1 = new Date();
System.out.println(d1);
// 根据毫秒创建指定时间
Date d2 = new Date(5000);
System.out.println(d2);
// 获取时间戳
long time = d1.getTime();
System.out.println(time);
2. SimpleDateFormat(日期格式化 / 解析)
负责 Date ↔ 字符串 互相转换
核心方法
String format(Date date):日期对象 → 格式化字符串Date parse(String source):时间字符串 → Date 对象(会抛出解析异常)
格式模板关键提醒 ⚠️
yyyy= 年、MM= 月(大写!小写 mm 代表分钟,写错会解析出错)、dd= 日、HH=24 小时、mm= 分、ss= 秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
// 格式化:Date转字符串
Date now = new Date();
String dateStr = sdf.format(now);
System.out.println(dateStr);
// 解析:字符串转Date
String birthday = "2000年08月01日 12:00:00";
Date parseDate = sdf.parse(birthday);
System.out.println(parseDate);
旧 API 致命缺点(面试高频)
SimpleDateFormat非线程安全,多线程并发会解析错乱- Date 对象可变,
setTime()修改会直接改变原对象,易产生数据污染 - API 设计混乱,月份从 0 开始计数,极易踩坑
三、JDK8 全新时间 API(推荐生产使用)
优势总结 ✅
- 对象不可变,增减时间返回新对象,原数据不会被修改
- 全部类线程安全,可在多线程共用格式化器
- API 拆分清晰:日期、时间、日期时间分离,语义明确
- 精确支持纳秒级时间,提供专用工具类计算时间间隔
1. 三大核心时间类
| 类名 | 作用 |
|---|---|
| LocalDate | 仅存储:年、月、日、星期 |
| LocalTime | 仅存储:时、分、秒、纳秒 |
| LocalDateTime | 完整日期时间:年月日 + 时分秒 |
获取对象两种方式
// 1. now():获取当前系统时间
LocalDate today = LocalDate.now();
LocalTime nowTime = LocalTime.now();
LocalDateTime nowDt = LocalDateTime.now();
// 2. of():自定义指定时间
LocalDate birth = LocalDate.of(2000, 8, 1);
LocalTime noon = LocalTime.of(12, 30, 0);
LocalDateTime fullTime = LocalDateTime.of(2025, 11, 11, 16, 30);
2. 时间信息获取方法
getYear()/getMonthValue()/getDayOfMonth():获取年、月、日getHour()/getMinute()/getSecond():获取时、分、秒getDayOfWeek():获取星期
3. 时间修改 / 增减方法(重点)
所有修改方法都会返回全新时间对象,原对象保持不变
- 时间修改:
withYear()、withMonth()、withHour()直接指定数值 - 时间增加:
plusDays()、plusMonths()、plusHours() - 时间减少:
minusYears()、minusWeeks()、minusSeconds()
LocalDateTime time = LocalDateTime.now();
// 增加3天
LocalDateTime after3Day = time.plusDays(3);
// 减少2个月
LocalDateTime before2Month = time.minusMonths(2);
// 修改年份为2000年
LocalDateTime resetYear = time.withYear(2000);
4. DateTimeFormatter 新式格式化工具
替代 SimpleDateFormat,线程安全
// 1. 创建格式化模板
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
// 格式化:时间对象 → 字符串
String dateStr = formatter.format(now);
System.out.println(dateStr);
// 解析:字符串 → LocalDateTime
String str = "2025-08-01 14:20:00";
LocalDateTime parseTime = LocalDateTime.parse(str, formatter);
System.out.println(parseTime);
5. 时间间隔计算工具类
| 工具类 | 适用场景 |
|---|---|
| Period | 计算两个日期 (LocalDate) 间隔:年、月、日 |
| Duration | 计算两个时间 (LocalTime/LocalDateTime) 间隔:时、分、秒、纳秒 |
| ChronoUnit | 通用间隔计算,可自由指定单位(年 / 月 / 天 / 小时 / 分钟) |
LocalDate birth = LocalDate.of(2000, 8, 1);
LocalDate today = LocalDate.now();
// 间隔多少年
long years = ChronoUnit.YEARS.between(birth, today);
// 间隔多少天
long days = ChronoUnit.DAYS.between(birth, today);
System.out.println("年龄:" + years + "岁,相隔" + days + "天");
四、新旧 API 核心对比总结
| 对比项 | JDK1.0 Date/SimpleDateFormat | JDK8 LocalDateTime 系列 |
|---|---|---|
| 线程安全 | SimpleDateFormat 不安全 | 全部类线程安全 |
| 对象可变性 | Date 可变,修改污染原数据 | 不可变,操作返回新对象 |
| API 设计 | 混乱,月份从 0 开始 | 语义清晰,无反常识设计 |
| 精度 | 毫秒 | 纳秒级高精度 |
| 间隔计算 | 无专用工具,手动计算 | Period/Duration/ChronoUnit 完善工具 |
五、开发避坑指南 ⚠️
- 格式符号区分大小写:
MM= 月份,mm= 分钟,写错会解析错乱 - SimpleDateFormat 禁止定义为全局静态变量,多线程会出现异常
- LocalDateTime 所有增减方法必须接收返回值,原对象不会变化
- 计算年月日间隔优先用 ChronoUnit,比 Period 使用更灵活
- 新项目统一使用 JDK8 时间 API,老旧 Date 仅做兼容历史代码
六、文末总结
JDK8 新增的时间 API 彻底解决了旧 Date 体系的各类缺陷,是现在企业项目的标准选择。日常开发日期格式化、时间加减、年龄 / 间隔计算,全部优先使用LocalDateTime + DateTimeFormatter + ChronoUnit组合,规避线程安全与可变对象带来的线上 bug。
更多推荐



所有评论(0)