Подтвердить что ты не робот

Почему 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
4b9b3361

Ответ 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, вы можете.

  • В Java 8 и более поздних версиях появился новый API.
  • В Java 6 и 7 получите The ThreeTen Backport, backport из новых классов (это ThreeTen для JSR-310, где современные API был сначала определен).
  • На Android, используйте Android-версию ThreeTen Backport. Его называют ThreeTenABP, и я думаю, что theres замечательное объяснение в этом вопросе: как использовать ThreeTenABP в Android-проекте.