Прежде всего, имейте в виду, что я застрял здесь с 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 по умолчанию является мягким и не генерирует исключений, даже если формат заданной даты может не соответствовать формату, настроенному для синтаксического анализа. Вы можете сделать его менее мягким, например:
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);
Таким образом, он проверит, действительно ли символы действительны для этой даты.
Цитата из JavaDocs: «По умолчанию синтаксический анализ является мягким: если входные данные не в форме, используемой методом форматирования этого объекта, но все же могут быть проанализированы как дата, то синтаксический анализ завершается успешно. Клиенты могут настаивать на строгом соблюдении формата вызов setLenient(false)".
Также обратите внимание, что в javadoc parse(String source) указано "Метод не может использовать весь текст данной строки", поэтому вы можете использовать перегрузку parse(String source, ParsePosition pos) и убедиться, что строковое значение точно соответствует формату даты, т. е. весь текст был использован.
Описание в ответе не точное. Мягкий SimpleDateFormatделает выдает исключение, если не может согласовать формат.
К вашему сведению, ужасно проблемные классы даты и времени, такие как java.util.Date, java.util.Calendar и java.text.SimpleDateFormat, теперь являются наследие, вытесненными классами Java.время, встроенными в Java 8 и более поздние версии. См. Руководство от Oracle.
Any idea what's wrong?
Это одно из мест, где SimpleDateFormat становится действительно проблематично. При попытке проанализировать 1980-04-25 с использованием шаблона yyyyMMdd он анализирует 1980 как год, как и ожидалось, затем -0 как месяц, поскольку он должен был состоять из двух символов, затем 4 как день месяца, хотя он также должен был быть двумя символами. Он игнорирует -25, потому что он выполнил синтаксический анализ.
А SimpleDateFormat со стандартными настройками также игнорирует отсутствие месяца -0. Он принимает его равным 0, то есть за месяц до января 1980 года, поэтому вы получаете декабрь 1979 года.
SimpleDateFormat общеизвестно хлопотно, а Date плохо спроектирован. Оба давно устарели. Я рекомендую вам не использовать SimpleDateFormat. Всегда.
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.
be aware I'm stuck with java6 here.
Я рекомендую вам не использовать
SimpleDateFormatиDate. Эти классы плохо спроектированы и давно устарели, а первые, как известно, доставляют много хлопот. Вместо этого используйтеLocalDateиDateTimeFormatter, оба из java.time, современный API даты и времени Java.