scala 为什么 SimpleDateFormat 解析不正确的日期?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15336200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does SimpleDateFormat parse incorrect date?
提问by Rishi
I have date in string format and I want to parse that into util date.
我有字符串格式的日期,我想将其解析为 util 日期。
var date ="03/11/2013"
I am parsing this as :
我将其解析为:
new SimpleDateFormat("MM/dd/yyyy").parse(date)
But the strange thing is that, if I am passing "03-08-201309 hjhkjhk" or "03-88-2013" or 43-88-201378", it does not throw error , it parses it.
但奇怪的是,如果我传递“03-08- 201309 hjhkjhk”或“03- 88-2013”或43-88-201378" ,它不会引发错误,它分析它。
For this now, I have to write regex pattern for checking whetehr input of date is correct or not. but why is it so ??
为此,我必须编写正则表达式模式来检查日期的输入是否正确。但为什么会这样??
Code :
代码 :
scala> val date="03/88/201309 hjhkjhk"
date: java.lang.String = 03/88/201309 hjhkjhk
scala> new SimpleDateFormat("MM/dd/yyyy").parse(date)
res5: java.util.Date = Mon May 27 00:00:00 IST 201309
回答by Jon Skeet
You should use DateFormat.setLenient(false):
你应该使用DateFormat.setLenient(false):
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
df.setLenient(false);
df.parse("03/88/2013"); // Throws an exception
I'm not sure that will catch everythingyou want - I seem to remember that even with setLenient(false)it's more lenient than you might expect - but it should catch invalid month numbers for example.
我不确定这是否会捕获您想要的所有内容- 我似乎记得即使setLenient(false)它比您预期的更宽松 - 但它应该会捕获例如无效的月份数字。
I don't think it will catch trailing text, e.g. "03/01/2013 sjsjsj". You could potentially use the overload of parsewhich accepts a ParsePosition, then check the current parse index after parsing has completed:
我认为它不会捕获尾随文本,例如“03/01/2013 sjsjsj”。您可以潜在地使用parse其接受 a的重载ParsePosition,然后在解析完成后检查当前解析索引:
ParsePosition position = new ParsePosition(0);
Date date = dateFormat.parse(text, position);
if (position.getIndex() != text.length()) {
// Throw an exception or whatever else you want to do
}
You should also look at the Joda TimeAPI which may well allow for a stricter interpretation - and is a generally cleaner date/time API anyway.
您还应该查看Joda TimeAPI,它很可能允许更严格的解释 - 并且通常是一个更清晰的日期/时间 API。
回答by Ole V.V.
Jon Skeet's answeris correct and was a good answer when it was written in 2013.
Jon Skeet 的答案是正确的,在 2013 年写的时候是一个很好的答案。
However, the classes you use in your question, SimpleDateFormatand Date, are now long outdated, so if someone got a similar issue with them today, IMHO the best answer would be to change to using the modern Java date & time API.
但是,你在你的问题中使用的类,SimpleDateFormat并且Date,现在早已过时的,所以如果有人得到了与他们今天类似的问题,恕我直言最好的答案将是改变使用现代的Java日期和时间API。
I am sorry I cannot write Scala code, so you will have to live with Java. I am using
很抱歉,我不会编写 Scala 代码,因此您将不得不使用 Java。我在用
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy");
The format pattern letters are the same as in your question, though the meaning is slightly different. DateTimeFormattertakes the number of pattern letters literally, as we shall see. Now we try:
格式模式字母与您的问题相同,但含义略有不同。DateTimeFormatter正如我们将看到的,从字面上取模式字母的数量。现在我们尝试:
System.out.println(LocalDate.parse(date, parseFormatter));
Results:
结果:
"03/11/2013"is parsed into2013-03-11as expected. I used the modernLocalDateclass, a class that represents a date without time-of-day, exactly what we need here.- Passing
"03/88/2013 hjhkjhk"gives aDateTimeParseExceptionwith the messageText '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10. Pretty precise, isn't it? The modern API has methods to parse only part of a string if that is what we want, though. "03/88/201309"givesText '03/88/201309' could not be parsed at index 6. We asked for a 4 digit year and gave it 6 digits, which leads to the objection. Apparently it detects and reports this error before trying to interpret 88 as a day of month.- It does object to a day of month of 88 too, though:
"03/88/2013"givesText '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88. Again, please enjoy how informative the message is. "03-08-2013"(with hyphens instead of slashes) givesText '03-08-2013' could not be parsed at index 2, not very surprising. Index 2 is where the first hyphen is.
"03/11/2013"被解析2013-03-11为预期的。我使用了现代LocalDate类,一个代表没有时间的日期的类,正是我们在这里需要的。- 传递
"03/88/2013 hjhkjhk"给一个DateTimeParseException同消息Text '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10。很精确,不是吗?不过,如果这是我们想要的,现代 API 有一些方法可以只解析字符串的一部分。 "03/88/201309"给Text '03/88/201309' could not be parsed at index 6. 我们要求 4 位数的年份并给它 6 位数,这导致了反对。显然,它会在尝试将 88 解释为一个月中的某一天之前检测并报告此错误。- 不过,它也反对 88 月的某一天:
"03/88/2013"给出Text '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88. 再次,请享受信息的丰富性。 "03-08-2013"(用连字符代替斜杠)给出Text '03-08-2013' could not be parsed at index 2,并不奇怪。索引 2 是第一个连字符所在的位置。
Jon Skeet explained that the outdated SimpleDateFormatcan be lenient or non-lenient. This is true for DateTimeFormattertoo, in fact it has 3 instead of 2 resolver styles, called ‘lenient', ‘smart' and ‘strict'. Since many programmers are not aware of this, though, I think they made a good choice of not making ‘lenient' the default (‘smart' is).
Jon Skeet 解释说,过时的SimpleDateFormat可以是宽容的,也可以是非宽容的。这也是如此DateTimeFormatter,事实上它有 3 种而不是 2 种解析器样式,称为“lenient”、“smart”和“strict”。但是,由于许多程序员没有意识到这一点,我认为他们做出了一个很好的选择,即不将“宽松”设为默认值(“智能”是)。
What if we wanted to make our formatter lenient?
如果我们想让格式化程序变得宽松怎么办?
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy")
.withResolverStyle(ResolverStyle.LENIENT);
Now it also parses "03/88/2013", into 2013-05-27. I believe this is what the old class would also have done: counting 88 days from the beginning of March gives May 27. The other error messages are still the same. In other words it still objects to unparsed text, to a 6 digit year and to hyphens.
现在它也将"03/88/2013",解析为2013-05-27. 我相信这也是老班会做的事情:从 3 月初开始计算 88 天是 5 月 27 日。其他错误消息仍然相同。换句话说,它仍然反对未解析的文本、6 位数字年份和连字符。
Question: Can I use the modern API with my Java version?
问:我可以在我的 Java 版本中使用现代 API 吗?
If using at least Java 6, you can.
如果至少使用 Java 6,则可以。
- In Java 8 and later the new API comes built-in.
- In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that's ThreeTen for JSR-310, where the modern API was first defined).
- On Android, use the Android edition of ThreeTen Backport. It's called ThreeTenABP, and I think that there's a wonderful explanation in this question: How to use ThreeTenABP in Android Project.
- 在 Java 8 和更高版本中,新的 API 是内置的。
- 在 Java 6 和 7 中获得ThreeTen Backport,新类的向后移植(即用于 JSR-310 的 ThreeTen,现代 API 最初定义在此处)。
- 在 Android 上,使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP,我认为这个问题有一个很好的解释:How to use ThreeTenABP in Android Project。

![找不到 scala.reflect.ClassManifest[T] 类型的证据参数的隐式值](/res/img/loading.gif)