SimpleDateFormat 存在严重问题。默认的 lenient 设置会产生垃圾答案,我想不出 lenient 有什么好处的情况。宽松设置不是对人类输入的日期变化产生合理解释的可靠方法。这不应该是默认设置。
如果可以,请改用 DateTimeFormatter,请参阅 Ole VV 的答案。这种较新的方法更优越,并产生线程安全和不可变的实例。如果您在线程之间共享 SimpleDateFormat 实例,它们可以产生垃圾结果而不会出现错误或异常。可悲的是,我建议的实现继承了这种不良行为。
禁用 lenient 只是解决方案的一部分。您仍然会得到在测试中难以捕捉到的垃圾结果。有关示例,请参见下面代码中的注释。
这是 SimpleDateFormat 的扩展,它强制进行严格的模式匹配。这应该是该类的默认行为。
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Extension of SimpleDateFormat that implements strict matching.
* parse(text) will only return a Date if text exactly matches the
* pattern.
*
* This is needed because SimpleDateFormat does not enforce strict
* matching. First there is the lenient setting, which is true
* by default. This allows text that does not match the pattern and
* garbage to be interpreted as valid date/time information. For example,
* parsing "2010-09-01" using the format "yyyyMMdd" yields the date
* 2009/12/09! Is this bizarre interpretation the ninth day of the
* zeroth month of 2010? If you are dealing with inputs that are not
* strictly formatted, you WILL get bad results. You can override lenient
* with setLenient(false), but this strangeness should not be the default.
*
* Second, setLenient(false) still does not strictly interpret the pattern.
* For example "2010/01/5" will match "yyyy/MM/dd". And data disagreement like
* "1999/2011" for the pattern "yyyy/yyyy" is tolerated (yielding 2011).
*
* Third, setLenient(false) still allows garbage after the pattern match.
* For example: "20100901" and "20100901andGarbage" will both match "yyyyMMdd".
*
* This class restricts this undesirable behavior, and makes parse() and
* format() functional inverses, which is what you would expect. Thus
* text.equals(format(parse(text))) when parse returns a non-null result.
*
* @author zobell
*
*/
public class StrictSimpleDateFormat extends SimpleDateFormat {
protected boolean strict = true;
public StrictSimpleDateFormat() {
super();
setStrict(true);
}
public StrictSimpleDateFormat(String pattern) {
super(pattern);
setStrict(true);
}
public StrictSimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) {
super(pattern, formatSymbols);
setStrict(true);
}
public StrictSimpleDateFormat(String pattern, Locale locale) {
super(pattern, locale);
setStrict(true);
}
/**
* Set the strict setting. If strict == true (the default)
* then parsing requires an exact match to the pattern. Setting
* strict = false will tolerate text after the pattern match.
* @param strict
*/
public void setStrict(boolean strict) {
this.strict = strict;
// strict with lenient does not make sense. Really lenient does
// not make sense in any case.
if (strict)
setLenient(false);
}
public boolean getStrict() {
return strict;
}
/**
* Parse text to a Date. Exact match of the pattern is required.
* Parse and format are now inverse functions, so this is
* required to be true for valid text date information:
* text.equals(format(parse(text))
* @param text
* @param pos
* @return
*/
@Override
public Date parse(String text, ParsePosition pos) {
Date d = super.parse(text, pos);
if (strict && d != null) {
String format = this.format(d);
if (pos.getIndex() + format.length() != text.length() ||
!text.endsWith(format)) {
d = null; // Not exact match
}
}
return d;
}
}