在Java中使用DateTimeFormatter
并启用严格模式(ResolverStyle.STRICT
)时,解析日期字符串"2025-06-01"
报错的根本原因是:模式字符串中的年份格式yyyy
被解释为YearOfEra
(纪元年份),而非Year
(公历年份) 。在严格模式下,解析器要求所有字段必须完整且一致,而LocalDate
需要Year
字段(而非YearOfEra
)来构建对象,导致转换失败。
解决方案:使用uuuu
代替yyyy
将模式中的年份占位符从yyyy
改为uuuu
,明确指定使用公历年份(Year
字段):
java
复制
下载
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")
.withResolverStyle(ResolverStyle.STRICT); // 严格模式
原因详解
-
字段差异:
-
yyyy
→ 解析为YearOfEra
(纪元年份),例如2025
表示"公元2025年"。 -
uuuu
→ 解析为Year
(公历年份),直接对应ISO年份体系。
-
-
严格模式的要求:
-
在
ResolverStyle.STRICT
下,解析器不会自动补全缺失字段(如纪元信息)。 -
错误信息中的
TemporalAccessor
包含:plaintext
复制
下载
{DayOfMonth=1, MonthOfYear=6, YearOfEra=2025}, ISO
缺少
Year
字段,只有YearOfEra
,无法直接构建LocalDate
。
-
-
uuuu
的优势:-
直接生成
Year
字段,与LocalDate
所需字段一致。 -
避免纪元歧义,尤其处理公元前/后的日期更安全。
-
验证修复后的代码
java
复制
下载
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
public class StrictDateParsing {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")
.withResolverStyle(ResolverStyle.STRICT); // 使用 uuuu
public static void main(String[] args) {
String dateStr = "2025-06-01";
LocalDate date = LocalDate.parse(dateStr, DATE_FORMATTER); // 解析成功
System.out.println(date); // 输出: 2025-06-01
}
}
关键结论
占位符 | 含义 | 严格模式下的行为 |
---|---|---|
yyyy |
纪元年份 | 解析为YearOfEra ,导致构建失败 |
uuuu |
公历年份 | 解析为Year ,与LocalDate 兼容 |
最佳实践 :
在严格模式中始终使用uuuu
表示年份 ,确保日期字段与LocalDate
的要求完全匹配,避免解析错误。
java
package com.weiyu.utils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.List;
public class DateUtils {
// 标准模式
// private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")
// 启用严格模式,使用 ResolverStyle.STRICT 严格模式,拒绝无效日期(如 2023-02-29),自动处理闰年、月份天数等复杂逻辑
// 启用严格模式,ofPattern必设置为"uuuu-MM-dd")
.withResolverStyle(ResolverStyle.STRICT);
/**
* 有效检查
* @param dateStr 字符串
* @return 提示信息
*/
public static String isValidDate(String dateStr) {
// 1. 空值检查
if (dateStr == null) return "日期字符串不能为null";
// 2. 空白检查
if (dateStr.isEmpty()) {
return "日期字符串不能为空";
}
return "";
}
/**
* 转换为 LocalDate 格式的日期
* @param dateStr 字符串
* @return LocalDate 格式的日期
*/
public static LocalDate parseDate(String dateStr) {
// 检查日期字符串格式
String msg = isValidDate(dateStr);
if (!msg.isEmpty()) {
throw new RuntimeException(msg);
}
try {
// 解析日期
return LocalDate.parse(dateStr, DATE_FORMATTER);
} catch (DateTimeParseException e) {
// 格式错误处理
throw new IllegalArgumentException("日期格式错误,应为 yyyy-MM-dd,并且为有效日期", e);
}
}
/**
* 转换为开始时间(当天的开始时刻 00:00:00)
* @param dateStr 字符串
* @return LocalDateTime 格式的日期时间
*/
public static LocalDateTime parseBeginDateTime(String dateStr) {
return parseDate(dateStr).atStartOfDay();
}
/**
* 转换为结束时间(当天的最后一刻 23:59:59.999999999)
* @param dateStr 字符串
* @return LocalDateTime 格式的日期时间
*/
public static LocalDateTime parseEndDateTime(String dateStr) {
return parseDate(dateStr).atTime(LocalTime.MAX);
}
/**
* 将字符数组转换为日期数组,如:["2025-06-01", "2026-06-10"] 转换为 [beginDate, endDate]
* @param dateStrList 字符数组
* @return 日期数组,只限开始日期和结束日期,[beginDate, endDate]
*/
public static List<LocalDate> parseDateRange(List<String> dateStrList) {
List<LocalDate> dateList = new ArrayList<>();
if (dateStrList == null || dateStrList.size() != 2) {
dateList.add(LocalDate.now());
dateList.add(LocalDate.now());
} else {
try {
dateList.add(parseDate(dateStrList.get(0)));
dateList.add(parseDate(dateStrList.get(1)));
} catch (DateTimeParseException e) {
dateList.add(LocalDate.now());
dateList.add(LocalDate.now());
}
}
// 检查日期逻辑关系,开始时间大于结束时间
if (dateList.get(0).isAfter(dateList.get(1))) {
LocalDate maxDate = dateList.get(0);
dateList.set(0, dateList.get(1));
dateList.set(1,maxDate);
}
return dateList;
}
/**
* 将字符数组转换为日期数组,如:["2025-06-01", "2026-06-10"] 转换为 [beginDateTime, endDateTime]
* @param dateStrList 字符数组
* @return 时间数组,只限开始时间和结束时间,[beginDateTime, endDateTime]
*/
public static List<LocalDateTime> parseDateTimeRange(List<String> dateStrList) {
List<LocalDate> dateList = parseDateRange(dateStrList);
List<LocalDateTime> dateTimeList = new ArrayList<>();
dateTimeList.add(dateList.get(0).atStartOfDay());
dateTimeList.add(dateList.get(1).atTime(LocalTime.MAX));
return dateTimeList;
}
}