一、前言

Java 处理日期分为两套 API:

  1. JDK1.0 老旧 APIDateSimpleDateFormat,存在线程不安全、设计不合理、修改时间会改变原对象等缺陷,生产环境尽量少用。
  2. JDK8 全新时间 APILocalDate/LocalTime/LocalDateTimeDateTimeFormatterChronoUnit,不可变对象、线程安全、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 ↔ 字符串 互相转换

核心方法
  1. String format(Date date):日期对象 → 格式化字符串
  2. 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 致命缺点(面试高频)

  1. SimpleDateFormat 非线程安全,多线程并发会解析错乱
  2. Date 对象可变,setTime() 修改会直接改变原对象,易产生数据污染
  3. API 设计混乱,月份从 0 开始计数,极易踩坑

三、JDK8 全新时间 API(推荐生产使用)

优势总结 ✅

  1. 对象不可变,增减时间返回新对象,原数据不会被修改
  2. 全部类线程安全,可在多线程共用格式化器
  3. API 拆分清晰:日期、时间、日期时间分离,语义明确
  4. 精确支持纳秒级时间,提供专用工具类计算时间间隔

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. 时间修改 / 增减方法(重点)

所有修改方法都会返回全新时间对象,原对象保持不变

  1. 时间修改:withYear()withMonth()withHour() 直接指定数值
  2. 时间增加:plusDays()plusMonths()plusHours()
  3. 时间减少: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 完善工具

五、开发避坑指南 ⚠️

  1. 格式符号区分大小写:MM= 月份,mm= 分钟,写错会解析错乱
  2. SimpleDateFormat 禁止定义为全局静态变量,多线程会出现异常
  3. LocalDateTime 所有增减方法必须接收返回值,原对象不会变化
  4. 计算年月日间隔优先用 ChronoUnit,比 Period 使用更灵活
  5. 新项目统一使用 JDK8 时间 API,老旧 Date 仅做兼容历史代码

六、文末总结

JDK8 新增的时间 API 彻底解决了旧 Date 体系的各类缺陷,是现在企业项目的标准选择。日常开发日期格式化、时间加减、年龄 / 间隔计算,全部优先使用LocalDateTime + DateTimeFormatter + ChronoUnit组合,规避线程安全与可变对象带来的线上 bug。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐