在 Java 8 引入的 java.time 包中,LocalDateLocalDateTime 是处理日期和时间的核心类。它们不可变、线程安全,并且提供了强大的 API 来处理日期时间。将字符串(String)解析为这些类型是开发中常见的需求,例如从用户输入、配置文件或网络接口接收日期时间字符串。

一、核心概念

1. LocalDate

  • 表示一个不带时区的日期,如 2025-08-04
  • 常用于表示生日、节假日等只关心日期的场景。
  • 不包含时间或时区信息。

2. LocalDateTime

  • 表示一个不带时区的日期和时间,如 2025-08-04T12:13:45
  • 常用于表示事件发生的具体时间点(但不涉及时区转换)。
  • 不包含时区信息,仅表示“本地”时间。

3. DateTimeFormatter

  • 用于格式化和解析日期时间对象的类。
  • 提供了预定义的格式器(如 ISO_LOCAL_DATE),也支持自定义模式(如 yyyy-MM-dd HH:mm:ss)。
  • StringLocalDate/LocalDateTime 之间转换的关键桥梁。

二、操作步骤(非常详细)

✅ 将 String 转为 LocalDate

步骤 1:导入必要的类

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

步骤 2:准备日期字符串

String dateString = "2025-08-04"; // 示例输入

步骤 3:选择或创建 DateTimeFormatter

  • 情况 A:使用标准 ISO 格式(推荐)

    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; // 默认格式:yyyy-MM-dd
    
  • 情况 B:使用自定义格式

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    

步骤 4:调用 LocalDate.parse() 方法

try {
    LocalDate localDate = LocalDate.parse(dateString, formatter);
    System.out.println("解析结果:" + localDate); // 输出:2025-08-04
} catch (Exception e) {
    System.err.println("解析失败:" + e.getMessage());
}

完整示例代码:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class StringToLocalDate {
    public static void main(String[] args) {
        String input = "2025-08-04";
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
        LocalDate date = LocalDate.parse(input, formatter);
        System.out.println(date); // 2025-08-04
    }
}

✅ 将 String 转为 LocalDateTime

步骤 1:导入必要的类

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

步骤 2:准备日期时间字符串

String dateTimeString = "2025-08-04T12:13:45"; // ISO 格式
// 或者自定义格式:"2025-08-04 12:13:45"

步骤 3:选择或创建 DateTimeFormatter

  • 情况 A:ISO 标准格式

    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; // yyyy-MM-dd'T'HH:mm:ss
    
  • 情况 B:自定义格式

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    

步骤 4:调用 LocalDateTime.parse()

try {
    LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, formatter);
    System.out.println("解析结果:" + localDateTime);
} catch (Exception e) {
    System.err.println("解析失败:" + e.getMessage());
}

完整示例代码:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class StringToLocalDateTime {
    public static void main(String[] args) {
        String input = "2025-08-04 12:13:45";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime dt = LocalDateTime.parse(input, formatter);
        System.out.println(dt); // 2025-08-04T12:13:45
    }
}

三、常见错误与异常

错误类型 原因 解决方案
DateTimeParseException 字符串格式与格式器不匹配 检查字符串内容和 ofPattern 是否一致
NullPointerException 输入字符串为 null 解析前进行 null 判断
UnsupportedTemporalTypeException 格式器试图解析不支持的字段(如仅日期格式器用于含时间的字符串) 使用匹配的格式器
格式错误(如 MM 写成 mm mm 表示分钟,MM 才表示月份 注意大小写:yMdHms 分别对应年月日时分秒

❌ 错误示例:

DateTimeFormatter wrong = DateTimeFormatter.ofPattern("yyyy-mm-dd"); // mm 是分钟!
LocalDate.parse("2025-08-04", wrong); // 抛出异常

✅ 正确写法:

DateTimeFormatter correct = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // MM 是月份

四、注意事项

  1. 线程安全DateTimeFormatter 是线程安全的,可以声明为 static final 共享使用。
  2. 大小写敏感HH(24小时制) vs hh(12小时制),MM(月) vs mm(分),ss(秒)。
  3. ISO 标准优先:若系统间交互,尽量使用 ISO 格式(如 yyyy-MM-ddyyyy-MM-dd'T'HH:mm:ss),避免歧义。
  4. 避免使用 SimpleDateFormat:它是旧 API,线程不安全,已被 DateTimeFormatter 取代。
  5. 不要忽略异常处理:生产环境必须捕获 DateTimeParseException

五、使用技巧

1. 预定义格式器(推荐使用)

DateTimeFormatter.ISO_LOCAL_DATE        // yyyy-MM-dd
DateTimeFormatter.ISO_LOCAL_TIME        // HH:mm:ss
DateTimeFormatter.ISO_LOCAL_DATE_TIME   // yyyy-MM-dd'T'HH:mm:ss

2. 自定义格式器(灵活适配)

.ofPattern("yyyy年MM月dd日")           // 中文格式
.ofPattern("dd.MM.yyyy HH:mm")         // 欧洲风格
.ofPattern("MMM dd, yyyy", Locale.US)  // 英文月份

3. 使用 Optional 安全解析

public static Optional<LocalDate> parseSafe(String text, DateTimeFormatter formatter) {
    try {
        return Optional.of(LocalDate.parse(text, formatter));
    } catch (DateTimeParseException e) {
        return Optional.empty();
    }
}

调用示例:

Optional<LocalDate> result = parseSafe("2025-08-04", DateTimeFormatter.ISO_LOCAL_DATE);
result.ifPresent(date -> System.out.println("成功解析:" + date));

4. 支持多种格式解析(策略模式)

public static LocalDate parseMultipleFormats(String text) {
    List<DateTimeFormatter> formatters = Arrays.asList(
        DateTimeFormatter.ISO_LOCAL_DATE,
        DateTimeFormatter.ofPattern("dd/MM/yyyy"),
        DateTimeFormatter.ofPattern("yyyy.MM.dd")
    );

    for (DateTimeFormatter f : formatters) {
        try {
            return LocalDate.parse(text, f);
        } catch (DateTimeParseException ignored) {}
    }
    throw new IllegalArgumentException("无法解析日期字符串:" + text);
}

六、最佳实践与性能优化

✅ 最佳实践

实践 说明
复用 DateTimeFormatter 声明为 private static final 字段,避免重复创建
优先使用 ISO 格式 提高系统兼容性和可读性
统一项目日期格式 在配置文件中定义全局格式,避免硬编码
封装解析逻辑 提供工具类方法,集中处理异常和日志
使用 OptionalResult 类型 显式表达可能失败的操作

🔧 性能优化建议

  1. 缓存格式器对象

    public class DateUtils {
        private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    }
    
  2. 避免频繁创建 SimpleDateFormat(已淘汰)

    • java.timeDateTimeFormatter 本身就是不可变且线程安全的,无需额外同步。
  3. 批量解析时预编译格式器

    • 虽然 DateTimeFormatter 本身已优化,但在高并发场景下仍建议复用。
  4. 考虑使用 jacksonfastjson 的日期绑定功能

    • 在 JSON 序列化/反序列化时,可通过注解自动转换:
      @JsonFormat(pattern = "yyyy-MM-dd")
      private LocalDate birthday;
      

七、总结:快速掌握要点

项目 建议
核心类 LocalDate, LocalDateTime, DateTimeFormatter
转换方法 .parse(String, formatter)
推荐格式 yyyy-MM-dd(日期)、yyyy-MM-dd HH:mm:ss(时间)
关键注意 大小写敏感(MMmm)、空值检查、异常处理
最佳实践 复用格式器、使用 ISO、封装工具类
性能提示 避免重复创建 formatter,推荐 static final