Почему SimpleDateFormat.parse не генерирует здесь исключение?

Прежде всего, имейте в виду, что я застрял здесь с java6.

Я пытаюсь получить дату из строки, которая может быть в разных форматах. По какой-то причине я не получаю ParseException, где я ожидал бы его...

    import java.text.SimpleDateFormat;
    import java.util.Date;

    public class test1{

      public static void main(String argc[]){
          System.out.println(parseAllDateFormats(argc[0]));
    }

      private static final String[] dateFormats = "yyyyMMdd,yyyy/MM/dd,yyyy-MM-dd,dd/MM/yyyy,dd-MM-yyyy,dd-MMM-yyyy,yyyy MM dd".split(",");
      public static Date parseAllDateFormats(String date) {
          if (date == null)
              return null;
          for (int f = 0; f < dateFormats.length; f++) {
              String format = dateFormats[f];
              try {
                  SimpleDateFormat dateFormat = new SimpleDateFormat(format);
    System.out.println("trying " + format);                
                  return dateFormat.parse(date);
              }
              catch (Exception e) {}
          }
          return null;
      }

    }

бежать: Java test1 1980-04-25

Я ожидаю получить:

trying yyyyMMdd
trying yyyy/MM/dd
trying yyyy-MM-dd
Fri Apr 25 00:00:00 EST 1980

Но я получаю только:

trying yyyyMMdd
Tue Dec 04 00:00:00 EST 1979

Есть идеи, что случилось?

Я рекомендую вам не использовать SimpleDateFormat и Date. Эти классы плохо спроектированы и давно устарели, а первые, как известно, доставляют много хлопот. Вместо этого используйте LocalDate и DateTimeFormatter, оба из java.time, современный API даты и времени Java.

Ole V.V. 27.07.2019 08:09

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

Ole V.V. 27.07.2019 09:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
2
1 255
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

SimpleDateFormat по умолчанию является мягким и не генерирует исключений, даже если формат заданной даты может не соответствовать формату, настроенному для синтаксического анализа. Вы можете сделать его менее мягким, например:

SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);

Таким образом, он проверит, действительно ли символы действительны для этой даты.

Цитата из JavaDocs: «По умолчанию синтаксический анализ является мягким: если входные данные не в форме, используемой методом форматирования этого объекта, но все же могут быть проанализированы как дата, то синтаксический анализ завершается успешно. Клиенты могут настаивать на строгом соблюдении формата вызов setLenient(false)".

Benjamin 26.07.2019 21:49

Также обратите внимание, что в javadoc parse(String source) указано "Метод не может использовать весь текст данной строки", поэтому вы можете использовать перегрузку parse(String source, ParsePosition pos) и убедиться, что строковое значение точно соответствует формату даты, т. е. весь текст был использован.

Andreas 26.07.2019 22:10

Описание в ответе не точное. Мягкий SimpleDateFormatделает выдает исключение, если не может согласовать формат.

Ole V.V. 27.07.2019 08:13

К вашему сведению, ужасно проблемные классы даты и времени, такие как java.util.Date, java.util.Calendar и java.text.SimpleDateFormat, теперь являются наследие, вытесненными классами Java.время, встроенными в Java 8 и более поздние версии. См. Руководство от Oracle.

Basil Bourque 27.07.2019 20:39
Ответ принят как подходящий

Any idea what's wrong?

Это одно из мест, где SimpleDateFormat становится действительно проблематично. При попытке проанализировать 1980-04-25 с использованием шаблона yyyyMMdd он анализирует 1980 как год, как и ожидалось, затем -0 как месяц, поскольку он должен был состоять из двух символов, затем 4 как день месяца, хотя он также должен был быть двумя символами. Он игнорирует -25, потому что он выполнил синтаксический анализ.

А SimpleDateFormat со стандартными настройками также игнорирует отсутствие месяца -0. Он принимает его равным 0, то есть за месяц до января 1980 года, поэтому вы получаете декабрь 1979 года.

SimpleDateFormat общеизвестно хлопотно, а Date плохо спроектирован. Оба давно устарели. Я рекомендую вам не использовать SimpleDateFormat. Всегда.

Java.время

private static final String[] dateFormats
        = "yyyyMMdd,yyyy/MM/dd,yyyy-MM-dd,dd/MM/yyyy,dd-MM-yyyy,dd-MMM-yyyy,yyyy MM dd"
                .split(",");

public static LocalDate parseAllDateFormats(String date) {
    if (date == null) {
        return null;
    }
    for (String format : dateFormats) {
        try {
            System.out.println("trying " + format);                
            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(format, Locale.ENGLISH);
            return LocalDate.parse(date, dateFormatter);
        }
        catch (DateTimeParseException e) {
            // Ignore, try next format
        }
    }
    return null;
}

Давайте попробуем это с вашей строкой примера:

    System.out.println(parseAllDateFormats("1980-04-25"));

Результат ожидаемый:

trying yyyyMMdd
trying yyyy/MM/dd
trying yyyy-MM-dd
1980-04-25

Я использую java.time, современный API даты и времени Java. По сравнению со старыми классами даты и времени работать с ними намного приятнее. Современный DateTimeFormatter может преобразовывать проанализированные значения в дату в трех стилях: строгом, умном и снисходительном. Смарт — это значение по умолчанию, где он будет принимать только номер месяца от 1 до 12. Также LocalDate.parse будет настаивать на анализе всей строки или выдаст исключение.

По сравнению с вашим кодом я также сделал несколько незначительных изменений. Для большинства целей я бы создал массив из DateTimeFormatter (не из String), но это не позволило бы мне напечатать trying yyyyMMdd, поэтому здесь я этого не сделал. Я поставил фигурные скобки в первом выражении if. Я указал локаль в форматере, потому что аббревиатуры месяцев различаются в разных языках, и я хочу контролировать, какой язык используется. Я улавливаю конкретное DateTimeParseException и добавляю комментарий, почему я его игнорирую, потому что иначе игнорирование исключения — это плохо, очень плохо. В нижней части вашего метода вы также должны рассмотреть возможность создания исключения, если формат не работает, а не возвращать null.

Ява 6

be aware I'm stuck with java6 here.

  • Классы Java.время встроены в Java 8 и более поздние версии, а также в Android 26 и более поздние версии.
  • ✅ Большая часть функций Java.время обратно перенесена на Java 6 и Java 7 в проекте ThreeTen-Backport.
  • Дальнейшая адаптация для более ранних версий Android (<26) в тридесятьABP. См. Как использовать ThreeTenABP….

Ссылки

Другие вопросы по теме