简体   繁体   English

Lenient SimpleDateFormat表现得很奇怪

[英]Lenient SimpleDateFormat acting strange

I understand that, in order to properly validate date strings, one must make DateFormat instances non-lenient to get all ParseExceptions from malformed dates. 我理解,为了正确验证日期字符串,必须使DateFormat实例非宽松,以便从格式错误的日期获取所有ParseExceptions。 But consider 但考虑一下

String  dubiousDate = "2014-04-01";
DateFormat  sdf = new SimpleDateFormat( "yyyyMMdd");
Date d;
try {
    d = sdf.parse( dubiousDate);
    System.out.println( dubiousDate + " -> " + d);
} catch ( ParseException e) {
    e.printStackTrace();
    System.err.println( dubiousDate + " failed");
}

this will give 这会给

2014-04-01 -> Wed Dec 04 00:00:00 CET 2013 2014-04-01 - > Wed Dec 04 00:00:00 CET 2013

Now I can understand that the lenient calendars try to be nice and accept funny negative numbers, but this interpretation looks like the -01 is considered as month, even though it appears last, where the days are. 现在我可以理解宽松的日历试图很好并接受有趣的负数,但这种解释看起来像-01被认为是月份,即使它出现在最后,日期也是如此。 And the -04 months become 04 days, with the minus ignored. -04个月变为04天,减去忽略。

In all leniency, why would this make sense to anyone? 在所有宽大处理中,为什么这对任何人都有意义?

I see another possible interpretation: 我看到另一种可能的解释:

In the pattern yyyyMMdd the month part is limited to exact two chars because there are no separators between the different numerical fields. 在模式yyyyMMdd中,月份部分限于精确的两个字符,因为不同的数字字段之间没有分隔符。 So "-0" will be seen as month which is just zero and is one month behind January yielding December in previous year. 因此,“-0”将被视为月份,仅为零,比1月份的一个月产生去年12月。

After having "parsed" the fake month, the day part comes with "4" stopping before the second minus char. 在“解析”假月之后,日期部分在第二个减去字符之前停止“4”。 The result is then the fourth of December. 结果是12月4日。

Finally, the remaining chars "-01" are simply ignored. 最后,剩下的字符“-01”被忽略了。 This is typical for the class SimpleDateFormat about how to handle non-digit trailing chars, for example see this code: 这是SimpleDateFormat类关于如何处理非数字尾随字符的典型情况,例如,请参阅以下代码:

String dubiousDate = "2014-04-01xyz";
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d;
try {
    d = sdf.parse(dubiousDate);
    System.out.println(dubiousDate + " -> " + d);
    // output: Tue Apr 01 00:00:00 CEST 2014
} catch (ParseException e) {
    e.printStackTrace();
    System.err.println(dubiousDate + " failed");
}

As thumb rule, with only two equal symbol chars MM or dd the parser will only consume up to at most two chars (if digits are found). 作为拇指规则,只有两个相等的符号字符MM或dd,解析器最多只消耗两个字符(如果找到数字)。

Some research about Java 8: 关于Java 8的一些研究:

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.parseLenient();
builder.append(DateTimeFormatter.ofPattern("yyyyMMdd"));
DateTimeFormatter dtf = builder.toFormatter();
String dubiousDate = "2014-04-01";
LocalDate date = LocalDate.parse(dubiousDate, dtf);
System.out.println(date);

According to JDK-8-documentation the formatter constructed this way should behave leniently, but unfortunately still throws an exception: 根据JDK-8文档,以这种方式构造的格式化程序应该表现得很宽松,但不幸的是仍然会抛出异常:

"Exception in thread "main" java.time.format.DateTimeParseException: Text '2014-04-01' could not be parsed at index 3" “线程中的异常”主“java.time.format.DateTimeParseException:无法在索引3处解析文本'2014-04-01'”

Best option would be in lenient case - theoretically - if the parser just ignores the minus chars. 最好的选择是在宽松的情况下 - 理论上 - 如果解析器只是忽略减去字符。 But obviously this is not possible with JSR-310 (still too strict). 但显然这对JSR-310来说是不可能的(仍然太严格)。 Well, SimpleDateFormat is lenient, but in rather a wrong way. 嗯, SimpleDateFormat很宽松,但方式错误。

This doesn't make sense. 这没有意义。 It sounds like a bug to me. 这对我来说听起来像个错误。

I think the right answer is to wait for Java 8 where dates are finally done right. 我认为正确的答案是等待最终完成日期的Java 8。 Your code, for example, could now change to something like what is below. 例如,您的代码现在可以更改为类似下面的内容。 And, Java will throw an exception, like it should. 并且,Java会抛出异常,就像它应该的那样。

import java.util.*;
import java.lang.*;
import java.io.*;

import java.text.DateFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
  public static void main(String[] args) {
    String dubiousDate = "2014-04-01";
    LocalDate d;
    try {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        d = LocalDate.parse(dubiousDate, formatter);
        System.out.println(dubiousDate + " -> " + d);
      }
      catch (Exception e) {
        e.printStackTrace();
        System.err.println(dubiousDate + " failed");
      }
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM