将字符串转换为 java.util.Date 对象是 Java 开发中的常见需求。由于 Date 本身不包含格式信息,它只是一个时间戳(自 1970-01-01 00:00:00 UTC 起的毫秒数),因此需要借助 格式化工具 来解析字符串。
关键类
| 类名 | 包路径 | 说明 |
|---|---|---|
SimpleDateFormat |
java.text.* |
传统方式,线程不安全,功能强大 |
DateTimeFormatter |
java.time.format.* |
Java 8+ 推荐,线程安全,类型安全 |
Date |
java.util.* |
存储时间点(已过时部分方法) |
LocalDateTime, ZonedDateTime |
java.time.* |
新时间 API,推荐使用 |
✅ 现代建议:优先使用
java.time包 +DateTimeFormatter
操作步骤(非常详细)
方法一:使用 SimpleDateFormat(传统方式)
步骤 1:导入必要的类
import java.text.SimpleDateFormat;
import java.util.Date;
步骤 2:定义日期格式模式
String pattern = "yyyy-MM-dd HH:mm:ss";
// 常见模式符号:
// yyyy - 四位年份
// MM - 两位月份(01-12)
// dd - 两位日期(01-31)
// HH - 24小时制小时(00-23)
// mm - 分钟(00-59)
// ss - 秒(00-59)
步骤 3:创建 SimpleDateFormat 实例
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
步骤 4:调用 parse() 方法转换
String dateString = "2025-08-15 10:30:45";
try {
Date date = sdf.parse(dateString); // 转换成功返回 Date 对象
System.out.println("转换结果: " + date);
} catch (ParseException e) {
System.err.println("解析失败: " + e.getMessage());
}
完整示例
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDate_SimpleDateFormat {
public static void main(String[] args) {
String input = "2025-08-15 10:30:45";
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
Date date = sdf.parse(input);
System.out.println("原始字符串: " + input);
System.out.println("转换后的Date: " + date);
} catch (ParseException e) {
System.err.println("格式错误,无法解析: " + e.getMessage());
}
}
}
方法二:使用 java.time API(推荐,Java 8+)
步骤 1:导入新时间 API 类
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
步骤 2:定义格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
步骤 3:解析为 LocalDateTime
String dateString = "2025-08-15 10:30:45";
LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
步骤 4:如需 java.util.Date,进行转换
import java.util.Date;
import java.time.ZoneId;
// LocalDateTime 转 Date
Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
完整示例
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
import java.util.Date;
public class StringToDate_Modern {
public static void main(String[] args) {
String input = "2025-08-15 10:30:45";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
try {
LocalDateTime ldt = LocalDateTime.parse(input, formatter);
Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDateTime: " + ldt);
System.out.println("转换为 Date: " + date);
} catch (Exception e) {
System.err.println("解析异常: " + e.getMessage());
}
}
}
常见错误
| 错误类型 | 示例 | 原因 | 修复方法 |
|---|---|---|---|
ParseException |
"2025/08/15" 用 "yyyy-MM-dd" 解析 |
格式不匹配(斜杠 vs 连字符) | 修改格式或字符串 |
NullPointerException |
sdf.parse(null) |
输入为 null | 提前判空 |
IllegalArgumentException |
ofPattern("HH:MM:ss") |
MM 错误(应为 mm 表示分钟) | 使用正确模式:mm |
| 时区混乱 | UTC 字符串转本地时间出错 | 未指定时区 | 使用 ZonedDateTime 或明确设置时区 |
| 线程安全问题 | 多线程共用一个 SimpleDateFormat |
SimpleDateFormat 非线程安全 |
使用 ThreadLocal 或改用 DateTimeFormatter |
注意事项
- ⚠️
SimpleDateFormat非线程安全:不要在多线程环境中共享同一个实例。 - ⚠️ 模式大小写敏感:
MM= 月份mm= 分钟SS= 毫秒(大写 S)ss= 秒
- ⚠️ 闰年、时区、夏令时:复杂日期需考虑这些因素,建议使用
ZonedDateTime。 - ⚠️ 默认时区:
Date.toString()使用本地时区显示,但内部时间是 UTC。 - ⚠️ 异常必须处理:
parse()方法抛出ParseException,必须try-catch。
使用技巧
技巧 1:预定义常用格式
public class DateFormats {
public static final DateTimeFormatter YYYY_MM_DD_HHMMSS =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final DateTimeFormatter ISO_DATE =
DateTimeFormatter.ISO_DATE;
}
技巧 2:支持多种格式尝试解析
public static Date parseFlexible(String dateStr) throws ParseException {
String[] patterns = {"yyyy-MM-dd", "dd/MM/yyyy", "yyyy年MM月dd日"};
for (String pattern : patterns) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(dateStr);
} catch (ParseException e) {
continue;
}
}
throw new ParseException("所有格式均不匹配", 0);
}
技巧 3:使用 Optional 避免异常
public static Optional<Date> safeParse(String str, String pattern) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return Optional.of(sdf.parse(str));
} catch (ParseException e) {
return Optional.empty();
}
}
最佳实践与性能优化
✅ 最佳实践
| 实践 | 说明 |
|---|---|
✅ 优先使用 java.time |
更清晰、安全、功能强大 |
✅ 使用 DateTimeFormatter 常量 |
避免重复创建,线程安全 |
| ✅ 封装解析逻辑 | 提供统一接口,降低耦合 |
| ✅ 明确指定时区 | 避免默认时区带来的歧义 |
| ✅ 输入校验 | 判空、长度检查等 |
⚙️ 性能优化
缓存
SimpleDateFormat实例(若必须使用):private static final ThreadLocal<SimpleDateFormat> SDF_THREADLOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));重用
DateTimeFormatter:它是不可变且线程安全的,可定义为static final。避免频繁创建对象:在循环中不要每次都
new SimpleDateFormat()。使用
Instant直接解析 ISO 格式:Instant instant = Instant.parse("2025-08-15T10:30:45Z"); // 高效批量处理时预编译格式:尤其在数据导入场景。
总结
| 维度 | 说明 |
|---|---|
| 核心工具 | SimpleDateFormat(旧)、DateTimeFormatter(新) |
| 关键步骤 | 定义格式 → 创建格式化器 → 调用 parse() → 处理异常 |
| 常见陷阱 | 格式不匹配、大小写错误、线程安全、时区问题 |
| 现代推荐 | LocalDateTime.parse(str, formatter) |
| 性能要点 | 缓存格式化器、避免重复创建、使用 Instant 解析标准格式 |
| 最佳选择 | Java 8+ 项目一律使用 java.time API |
✅ 一句话总结:字符串转
Date的核心是格式匹配,推荐使用java.time.LocalDateTime+DateTimeFormatter,避免SimpleDateFormat的线程安全问题,封装解析逻辑以提高代码健壮性。