将字符串转换为 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

注意事项

  1. ⚠️ SimpleDateFormat 非线程安全:不要在多线程环境中共享同一个实例。
  2. ⚠️ 模式大小写敏感
    • MM = 月份
    • mm = 分钟
    • SS = 毫秒(大写 S)
    • ss = 秒
  3. ⚠️ 闰年、时区、夏令时:复杂日期需考虑这些因素,建议使用 ZonedDateTime
  4. ⚠️ 默认时区Date.toString() 使用本地时区显示,但内部时间是 UTC。
  5. ⚠️ 异常必须处理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 常量 避免重复创建,线程安全
✅ 封装解析逻辑 提供统一接口,降低耦合
✅ 明确指定时区 避免默认时区带来的歧义
✅ 输入校验 判空、长度检查等

⚙️ 性能优化

  1. 缓存 SimpleDateFormat 实例(若必须使用):

    private static final ThreadLocal<SimpleDateFormat> SDF_THREADLOCAL = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
  2. 重用 DateTimeFormatter:它是不可变且线程安全的,可定义为 static final

  3. 避免频繁创建对象:在循环中不要每次都 new SimpleDateFormat()

  4. 使用 Instant 直接解析 ISO 格式

    Instant instant = Instant.parse("2025-08-15T10:30:45Z"); // 高效
    
  5. 批量处理时预编译格式:尤其在数据导入场景。


总结

维度 说明
核心工具 SimpleDateFormat(旧)、DateTimeFormatter(新)
关键步骤 定义格式 → 创建格式化器 → 调用 parse() → 处理异常
常见陷阱 格式不匹配、大小写错误、线程安全、时区问题
现代推荐 LocalDateTime.parse(str, formatter)
性能要点 缓存格式化器、避免重复创建、使用 Instant 解析标准格式
最佳选择 Java 8+ 项目一律使用 java.time API

一句话总结:字符串转 Date 的核心是格式匹配,推荐使用 java.time.LocalDateTime + DateTimeFormatter,避免 SimpleDateFormat 的线程安全问题,封装解析逻辑以提高代码健壮性。