1. java.util.Date
类
1.1 基本概念
Date
类表示特定的瞬间,精确到毫秒。它内部存储的是自 UTC 时间 1970 年 1 月 1 日 00:00:00 以来的毫秒数(称为 Unix 时间戳或纪元时间)。
1.2 常用构造方法
// 当前时间
Date now = new Date();
// 指定毫秒数
Date specificTime = new Date(1672531200000L); // 2023-01-01 00:00:00 UTC
// 已过时的构造方法(不推荐使用)
Date oldWay = new Date(123, 0, 1); // 2023年1月1日 (年份-1900, 月份0-11)
1.3 常用方法
Date date = new Date();
// 获取时间戳
long timestamp = date.getTime();
// 比较日期
boolean isBefore = date.before(anotherDate);
boolean isAfter = date.after(anotherDate);
// 修改时间(已过时,不推荐)
date.setTime(1672531200000L);
注意:
Date
类的许多方法(如setYear()
,getYear()
等)已过时,应避免使用。
2. java.text.SimpleDateFormat
类
SimpleDateFormat
是 DateFormat
的子类,用于格式化(日期 → 字符串)和解析(字符串 → 日期)日期。
2.1 基本用法
格式化(Date → String)
import java.text.SimpleDateFormat;
import java.util.Date;
Date now = new Date();
// 创建格式化器
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 格式化日期
String formattedDate = sdf.format(now);
System.out.println(formattedDate); // 输出: 2025-08-15 10:47:00 (示例)
解析(String → Date)
String dateString = "2023-01-01 12:30:45";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date parsedDate = sdf.parse(dateString);
System.out.println(parsedDate); // 输出: Sun Jan 01 12:30:45 CST 2023
} catch (ParseException e) {
e.printStackTrace();
}
2.2 常用日期格式模式
模式 | 描述 | 示例 |
---|---|---|
yyyy |
四位年份 | 2025 |
MM |
两位月份 | 08 |
dd |
两位日期 | 15 |
HH |
24小时制小时 | 10 |
mm |
分钟 | 47 |
ss |
秒 | 00 |
SSS |
毫秒 | 123 |
E |
星期几(短) | Fri |
EEE |
星期几(短) | Fri |
EEEE |
星期几(长) | Friday |
a |
上午/下午 | AM |
z |
时区 | CST |
Z |
RFC 822 时区 | +0800 |
常见格式示例:
// 常用格式
new SimpleDateFormat("yyyy-MM-dd"); // 2025-08-15
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2025-08-15 10:47:00
new SimpleDateFormat("dd/MM/yyyy"); // 15/08/2025
new SimpleDateFormat("yyyy年MM月dd日"); // 2025年08月15日
new SimpleDateFormat("EEEE, MMMM dd, yyyy"); // Friday, August 15, 2025
3. 使用技巧
3.1 设置时区
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// 或
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
3.2 设置本地化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 EEEE", Locale.CHINESE);
3.3 自定义异常处理
public static Date parseDateSafely(String dateStr, String pattern) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(dateStr);
} catch (ParseException e) {
System.err.println("日期解析失败: " + dateStr);
return null;
}
}
4. 常见错误
4.1 ParseException
// 错误:字符串格式与模式不匹配
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2023/01/01"); // 抛出 ParseException
// 正确:确保格式匹配
Date date = sdf.parse("2023-01-01");
4.2 大小写敏感
// 错误:使用小写 mm 表示月份
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd"); // mm 是分钟!
// 正确:使用 MM 表示月份
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
4.3 线程安全问题
// ❌ 错误:SimpleDateFormat 不是线程安全的
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// ✅ 正确做法:
// 1. 每次使用都创建新的实例
// 2. 使用 ThreadLocal
// 3. 使用同步块
// 4. 使用 Java 8+ 的 DateTimeFormatter
4.4 闰秒和夏令时问题
SimpleDateFormat
在处理闰秒和夏令时转换时可能产生意外结果,需特别注意。
5. 注意事项
- 线程安全性:
SimpleDateFormat
不是线程安全的。在多线程环境中共享同一个实例会导致数据混乱。 - 性能:频繁创建
SimpleDateFormat
实例有一定开销,建议在单线程环境中重用,或使用ThreadLocal
。 - 时区处理:默认使用系统时区,跨时区应用需显式设置。
- 闰年处理:正确处理闰年(2月29日),但需确保输入格式正确。
- 国际化:不同 Locale 下的月份和星期名称显示不同。
6. 最佳实践与性能优化
6.1 线程安全解决方案
使用 ThreadLocal
private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String formatDate(Date date) {
return DATE_FORMATTER.get().format(date);
}
public static Date parseDate(String dateStr) throws ParseException {
return DATE_FORMATTER.get().parse(dateStr);
}
使用同步
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static synchronized Date parseDate(String dateStr) throws ParseException {
return sdf.parse(dateStr);
}
6.2 预定义格式常量
public class DateFormats {
public static final String DATE = "yyyy-MM-dd";
public static final String DATETIME = "yyyy-MM-dd HH:mm:ss";
public static final String TIMESTAMP = "yyyy-MM-dd HH:mm:ss.SSS";
// 使用
SimpleDateFormat sdf = new SimpleDateFormat(DATE);
}
6.3 使用 Java 8+ DateTimeFormatter
(推荐)
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String formatted = now.format(formatter);
// 解析
LocalDateTime parsed = LocalDateTime.parse("2025-08-15 10:47:00", formatter);
优势:不可变、线程安全、功能更强大、API 更清晰。
6.4 性能优化建议
- 避免在循环中创建
SimpleDateFormat
:在循环外创建并重用(单线程环境)。 - 使用缓存:对于常用格式,可以缓存
SimpleDateFormat
实例(配合ThreadLocal
)。 - 考虑使用
FastDateFormat
(Apache Commons Lang):import org.apache.commons.lang3.time.FastDateFormat; FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); String formatted = fdf.format(date);
7. 总结
特性 | Date + SimpleDateFormat |
java.time (Java 8+) |
---|---|---|
线程安全 | SimpleDateFormat 不安全 |
所有类都不可变且线程安全 |
API 清晰度 | 较混乱,方法过时 | 清晰、直观 |
功能完整性 | 基本功能 | 丰富(时区、周期、持续时间等) |
性能 | 中等 | 通常更好 |
推荐程度 | 维护旧代码 | 新项目首选 |
核心要点:
Date
:表示时间点,内部是毫秒时间戳。SimpleDateFormat
:用于格式化和解析,但不是线程安全的。- 格式模式:注意大小写(
MM
月份 vsmm
分钟)。 - 线程安全:多线程环境下必须使用
ThreadLocal
或同步。 - 现代替代:强烈推荐在新项目中使用
java.time
包(LocalDateTime
,ZonedDateTime
,DateTimeFormatter
)。