在 Java 8 引入的 java.time
包中,LocalDate
和 LocalDateTime
是处理日期和时间的核心类。它们不可变、线程安全,并且提供了强大的 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
)。 - 是
String
与LocalDate
/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 是月份
四、注意事项
- 线程安全:
DateTimeFormatter
是线程安全的,可以声明为static final
共享使用。 - 大小写敏感:
HH
(24小时制) vshh
(12小时制),MM
(月) vsmm
(分),ss
(秒)。 - ISO 标准优先:若系统间交互,尽量使用 ISO 格式(如
yyyy-MM-dd
和yyyy-MM-dd'T'HH:mm:ss
),避免歧义。 - 避免使用
SimpleDateFormat
:它是旧 API,线程不安全,已被DateTimeFormatter
取代。 - 不要忽略异常处理:生产环境必须捕获
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 格式 | 提高系统兼容性和可读性 |
统一项目日期格式 | 在配置文件中定义全局格式,避免硬编码 |
封装解析逻辑 | 提供工具类方法,集中处理异常和日志 |
使用 Optional 或 Result 类型 |
显式表达可能失败的操作 |
🔧 性能优化建议
缓存格式器对象
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"); }
避免频繁创建
SimpleDateFormat
(已淘汰)java.time
的DateTimeFormatter
本身就是不可变且线程安全的,无需额外同步。
批量解析时预编译格式器
- 虽然
DateTimeFormatter
本身已优化,但在高并发场景下仍建议复用。
- 虽然
考虑使用
jackson
或fastjson
的日期绑定功能- 在 JSON 序列化/反序列化时,可通过注解自动转换:
@JsonFormat(pattern = "yyyy-MM-dd") private LocalDate birthday;
- 在 JSON 序列化/反序列化时,可通过注解自动转换:
七、总结:快速掌握要点
项目 | 建议 |
---|---|
核心类 | LocalDate , LocalDateTime , DateTimeFormatter |
转换方法 | .parse(String, formatter) |
推荐格式 | yyyy-MM-dd (日期)、yyyy-MM-dd HH:mm:ss (时间) |
关键注意 | 大小写敏感(MM ≠ mm )、空值检查、异常处理 |
最佳实践 | 复用格式器、使用 ISO、封装工具类 |
性能提示 | 避免重复创建 formatter ,推荐 static final |