问题描述
String datestr = "2021-01-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
try {
System.out.println(sdf.parse(datestr));
} catch (ParseException e) {
e.printstacktrace();
}
由于日期字符串 2021-01-01
与格式模式字符串 yyyy-MM
不匹配,因此我已经预料到并且想要一个例外。
控制台打印 Fri Jan 01 00:00:00 CST 2021
。
相反的情况确实会导致预期的异常。 我让代码看起来像这样
String datestr = "2021-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
System.out.println(sdf.parse(datestr));
} catch (ParseException e) {
e.printstacktrace();
}
控制台打印错误信息:Unparseable date: "2021-01"
谁能告诉我为什么?
解决方法
您正在描述两种不同的场景:
- 您的输入文本包含的数据更多 比模式消耗的数据多,额外的数据在末尾。
- 您的输入文本包含的数据少于模式所消耗的数据。
#1 不是错误,因为 parse(String)
被明确记录为从输入的开头读取直到足够:
该方法可能不会使用给定字符串的整个文本。
#2 是一个错误,因为您的模式说它需要额外的数据(即 -
后跟一天)并且数据丢失。
tl;博士
您的格式模式必须适合您的输入文本。在第一种情况下,"2021-01-01"
与 "yyyy-MM"
不匹配。在第二种情况下,"2021-01"
与 "yyyy-MM-dd"
不匹配。
改用 java.time 类,因为与遗留类 SimpleDateFormat
和 Date
搏斗是不明智的。也更简单,不需要格式化模式。
YearMonth
.from(
LocalDate.parse( "2021-01-01" )
)
.toString()
看到这个code run live at IdeOne.com。
2021-01
并解析年月:
YearMonth.parse( "2021-01" )
详情
您正在使用多年前被现代 java.time 类取代的糟糕的日期时间类。 Sun、Oracle 和 JCP 社区在一致采用 JSR 310 后放弃了这些关闭。
您的输入恰好使用标准的 ISO 8601 格式。 java.time 类在解析/生成文本时默认使用这些格式。所以不需要指定格式模式。
LocalDate ld = LocalDate.parse( "2021-01-01" ) ;
YearMonth ym = YearMonth.from( ld ) ;
String output = ym.toString() ;
至于解析 "2021-01"
的输入:
YearMonth ym = YearMonth.parse( "2021-01" ) ;
,
这是一个适度的补充。 Basil Bourque 已经为您的工作展示了出色的现代解决方案,Generous Badger 解释了为什么您的代码没有按照您预期的方式运行。
如果您将错误格式的字符串输入到每个代码示例中,Basil Bourque 的代码确实会抛出预期的异常。对于您询问的代码的现代版本:
String datestr = "2021-01-01";
YearMonth.parse(datestr);
结果是:
线程“main”中的异常java.time.format.DateTimeParseException: 无法解析文本“2021-01-01”,在索引 7 处找到未解析的文本
也请享受异常消息的精确程度。
如果您选择使用显式格式化程序,也会发生同样的事情:
YearMonth.parse(datestr,DateTimeFormatter.ofPattern("uuuu-MM"));
线程“main”中的异常java.time.format.DateTimeParseException: 无法解析文本“2021-01-01”,在索引 7 处找到未解析的文本
不要使用 SimpleDateFormat
SimpleDateFormat
有很多令人困惑和设计不当的特性,您试图在两个代码示例中使用该类。您只被其中两个击中:
- 正如 Generous Badger 解释的那样,它可能不会解析提供给它的整个字符串,默认会留下未解析的文本。
- 它为未解析的字段创建默认值。在您的情况下,它默认为 JVM 的默认时区的每月 1 日 00:00:00。虽然可以指示 java.time 为不在解析字符串中的字段应用默认值,但这需要在 Java 代码中显式指定它们,从而导致更可预测且不那么令人惊讶的行为。
其他人已经提到了为什么会发生这种情况(Java 解析器在获得所需的一切时停止解析),并且还解释了该 API 较旧且不符合人体工程学。也就是说,您可以使用 ParsePosition
类做您想做的事情。
String datestr = "2021-01-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
ParsePosition pp = new ParsePosition(0);
sdf.parse(datestr,pp);
if(pp.getIndex() < dateStr.length()) {
throw new ParseException("Parsing failed...");
}