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

Java 8 LocalDateTime обрабатывает недопустимую дату

Я хотел проверить дату на стороне клиента, поэтому я написал следующий код. Но вместо получения исключения я получаю правильный объект даты для строки даты 31-го февраля, что явно недействительная дата.

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/yyyy";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (Exception e) {
            // Throw invalid date message
        }
    }
}

Выход: 2015-02-28T11: 30: 59

Кто-нибудь знает, почему LocalDateTime анализирует эту дату вместо того, чтобы бросать исключение.

4b9b3361

Ответ 1

Вам просто нужен строгий ResolverStyle.

Разбор текстовой строки происходит в два этапа. Фаза 1 представляет собой основной синтаксический анализ текста в соответствии с полями, добавленными к строителю. Фаза 2 разрешает проанализированные пары значений поля в дату и/или временные объекты. Этот стиль используется для управления тем, как выполняется этап 2, разрешая.

Пример кода - где withResolverStyle(ResolverStyle.STRICT) - важное изменение, а также использование uuuu, а не yyyy (где uuuu - "год", а "yyyy" - "год эры", и поэтому неоднозначный):

import java.time.*;
import java.time.format.*;
import java.util.*;

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/uuuu";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter
            .ofPattern(dateFormat, Locale.US)
            .withResolverStyle(ResolverStyle.STRICT);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (DateTimeParseException e) {
            // Throw invalid date message
            System.out.println("Exception was thrown");
        }
    }
}

Ответ 2

Java 8 DateTimeFormatter использует yyyy для обозначения YEAR_OF_ERA, а uuuu означает YEAR. Вам нужно изменить строку шаблона следующим образом:

String dateFormat = "HH:mm:ss MM/dd/uuuu";

По умолчанию для параметра DateTimeFormatter используется стиль распознавателя SMART, но вы хотите, чтобы он использовал стиль распознавателя STRICT. Измените код инициализации dateTimeFormatter следующим образом:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
                                                       .withResolverStyle(ResolverStyle.STRICT);

Ответ 3

try {
    SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
    df.setLenient(false);
    System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
  System.out.println(e);
}

Я нашел одно решение распознать дату как действительную дату с DateFormat.setLenient(boolean). Если вы попытаетесь проанализировать какую-либо недействительную дату, она выдает исключение синтаксического анализа.

Edit:

Java 8, но это вызовет исключение, если месяц не находится между 1 и 12, если день больше, чем 32. Точно не работает. Но в течение месяца он работает.

try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}

Вывод:

java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
 parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32

Ответ 4

Это не округление. В феврале никогда не было 31 дня, и невозможно использовать проверяющий объект даты/времени для представления дня, который не существует.

В результате он принимает неверный ввод и дает наилучшее приближение к правильной дате (последняя дата февраля того же года).

SimpleDateFormat наследует от DateFormat, который имеет на нем метод setLenient(boolean value). Я ожидал бы, что если вы вызвали setLenient(true) до синтаксического анализа, он, вероятно, больше пожаловался бы, как подробно описано в javadocs.

Ответ 5

LocalDateTime.parse будет выдавать ошибку только в том случае, если строка, содержащая строки, содержит недопустимые символы, количество дней, превышающее 31 или месяц, превышающее 12.

Например, если вы изменили свой код как таковой:

String dateString = "11:30:59 0zz2/31/2015";

исключение будет вызвано недействительными символами "zz" в пределах вашей даты. Что касается того, почему это "округление" даты так сказать, что я не знаю.

Источник: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-