如何防止SimpleDateFormat解析错误的格式化日期?

时间:2016-09-05 12:05:31

标签: java date simpledateformat

我使用SimpleDateFormat将字符串解析为Date个对象,我想知道为什么结果不符合我的预期。

例如:

DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

Date date = yyyyMMdd.parse("20100725");
System.out.println(date);

按预期工作和输出

Sun Jul 25 00:00:00 CEST 2010

但是

Date date = yyyyMMdd.parse("2010-07-25");
System.out.println(date);

也可以工作和输出

Mon Dec 07 00:00:00 CET 2009

我预计会有ParseException,但似乎SimpleDateFormat会将月份-07和日期-25解释为负数。首先,我无法弄清楚它是如何到达12月7日的。所以我尝试了另一个值:

Date date = yyyyMMdd.parse("2010-7-25");
System.out.println(date);

并且它outpus

Sun Apr 05 00:00:00 CEST 2009

所以它似乎以某种方式从7中减去2010个月,这可能是5月1日,25天所以结果是2009年4月5日。

您在服务实现中使用模式yyyyMMdd的图像,而某个客户端意外地将日期发送为yyyy-MM-dd。你不会得到例外。相反,你会得到完全不同的日期。我猜这不是你所期望的。

E.g。

String clientData = "2010-05-23";

DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date parsedDate = yyyyMMdd.parse(clientData);

System.out.println("Client  : " + clientData);
System.out.println("Service : " + yyyyMMdd.format(parsedDate));

我错过了什么吗?

如何阻止SimpleDateFormat解析错误的'日期吗

当然我可以先使用正则表达式进行检查,但是有更好的方法吗?

4 个答案:

答案 0 :(得分:5)

使用SimpleDateFormat.setLenient(false);获取例外。否则它将尝试尽可能最好地解析输入,这通常是错误的。

出于某种原因,他们认为默认情况下宽大处理应该是真的,但那是hardly a surprise

  

指定日期/时间解析是否宽松。同   宽松解析,解析器可以使用启发式来解释输入   与该对象的格式不完全匹配。严格要求   解析时,输入必须与此对象的格式匹配。

答案 1 :(得分:2)

SimpleDateFormat.setLenient(false);

需要做什么,或者尝试解析输入,如您所知,这并不总是有效。使用上面的函数,编译器将严格格式化。

答案 2 :(得分:1)

accepted Answer by Cayman是正确的:默认解析是宽容的问题。

java.time

您正在使用现在由java.time类取代的麻烦的旧日期时间类。

java.time中没有这种默认的宽大问题。如果输入与格式化模式不完全匹配,则抛出DateTimeParseException

LocalDate类表示没有时间且没有时区的仅限日期的值。

ISO 8601格式

对于YYYY-MM-DD的标准ISO 8601格式输入,只需直接拨打parse

String input = "2010-05-23";
try {
    LocalDate  ld = LocalDate.parse( input ); // Expects standard ISO 8601 input format.
} catch ( DateTimeParseException e ) {
    …
}

“基本”ISO 8601格式

ISO 8601标准允许“基本”格式,以最大限度地减少分隔符的使用。不是我推荐这些变化,但它们存在。

目前,java.time仅预定义了这些“基本”变体中的一个DateTimeFormatter.BASIC_ISO_DATE

String input = "20100725";
try {
    LocalDate  ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE ); 
} catch ( DateTimeParseException e ) {
    …
}

自定义格式

对于其他格式,请指定格式化程序。

String input = "2010/07/25";
try {
    DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/MM/dd" );
    LocalDate  ld = LocalDate.parse( input , f ); // Custom format.
} catch ( DateTimeParseException e ) {
    …
}

本地化格式

或者让java.time确定本地化格式。

String input = … ;
try {
    Locale l = Locale.CANADA_FRENCH ; 
    DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( l );
    LocalDate  ld = LocalDate.parse( input , f ); // Localized format.
} catch ( DateTimeParseException e ) {
    …
}

答案 3 :(得分:0)

首先,如果你想解析String" 2010-05-23"你的面具应该是" yyyy-MM-dd"而不是" yyyyMMdd"。第二个SimpleDateFormat存在严重问题,因为它不是Thread安全的。如果你使用java 8,那么使用learn并使用new package" java.time"。如果您使用早期版本8之前的任何Java,则使用其他一些框架来解析日期。其中最受欢迎的是Joda时间。效果更好。