Почему SimpleDateFormat анализирует неправильную дату?
У меня есть дата в строковом формате, и я хочу проанализировать это на дату использования.
var date ="03/11/2013"
Я разбираю это как:
new SimpleDateFormat("MM/dd/yyyy").parse(date)
Но странно, что если я прохожу "03-08- 201309 hjhkjhk" или "03- 88 -2013" или 43 -88-201378 ", он не выдает ошибки, он анализирует его.
Для этого сейчас я должен написать шаблон регулярного выражения для проверки правильности ввода даты даты.
но почему это так?
Код:
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
Ответы
Ответ 1
Вы должны использовать DateFormat.setLenient(false)
:
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
df.setLenient(false);
df.parse("03/88/2013"); // Throws an exception
Я не уверен, что поймаю все, что вы захотите, - я, кажется, помню, что даже с setLenient(false)
он более мягкий, чем вы могли ожидать, но он должен ловить неверные номера месяцев, например.
Я не думаю, что он поймает конечный текст, например. "03/01/2013 sjsjsj". Вы могли бы использовать перегрузку parse
, которая принимает 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
}
Вы также должны посмотреть API Joda Time, который вполне может позволить более строгую интерпретацию - и, как правило, это более чистый API-интерфейс даты/времени.
Ответ 2
Ответ Jon Skeets является правильным и был хорошим ответом, когда он был написан в 2013 году.
Тем не менее, классы, которые вы используете в своем вопросе, SimpleDateFormat
и Date
, теперь устарели, поэтому, если у кого-то появилась аналогичная проблема с ними сегодня, IMHO лучшим ответом будет переход на использование современный API дат и времени Java.
Мне жаль, что я не могу написать код Scala, поэтому вам придется жить с Java. Я использую
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy");
Буквы шаблонов формата такие же, как в вашем вопросе, хотя значение немного отличается. DateTimeFormatter
берет число букв шаблона буквально, как мы увидим. Теперь мы попробуем:
System.out.println(LocalDate.parse(date, parseFormatter));
Результаты:
-
"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
. Мы попросили четырехзначный год и дали ему 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 - это первый дефис.
Джон Считал объяснил, что устаревший SimpleDateFormat
может быть снисходительным или не снисходительным. Это верно и для DateTimeFormatter
, на самом деле у него есть 3 вместо 2 стилей резольвера, называемых "мягкий", "умный" и "строгий". Поскольку многие программисты не знают об этом, я думаю, что они сделали хороший выбор: "смягчить дефолт" ( "умный" ).
Что, если мы хотим сделать наш форматирующий снисходительным?
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy")
.withResolverStyle(ResolverStyle.LENIENT);
Теперь он также анализирует "03/88/2013"
, на 2013-05-27
. Я считаю, что это и сделал старый класс: отсчет 88 дней с начала марта дает 27 мая. Другие сообщения об ошибках все те же. Другими словами, он по-прежнему ставит под вопрос непроверенный текст, до шестизначного года и дефисов.
Вопрос: могу ли я использовать современный API с моей версией Java?
Если вы используете хотя бы Java 6, вы можете.