java.text.ParseException: неразборчивая дата

Я получаю следующую ошибку: «java.text.ParseException: Unparseable date: «31 августа 09:53:19 2011»» в этом формате: new SimpleDateFormat("MMM dd HH:mm:ss yyyy");

Кто-нибудь видит проблему?


person Stijn.V    schedule 31.08.2011    source источник
comment
Выглядит хорошо для меня. Убедитесь, что вы используете объект SimpleDateFormat, созданный с помощью показанного кода, для фактического анализа заданной даты.   -  person    schedule 31.08.2011
comment
Важный совет: всегда указывайте явно как Locale, так и часовой пояс в вашей работе с датой и временем, а не неявно полагайтесь на текущее значение по умолчанию JVM. Оба могут измениться в любой момент во время выполнения.   -  person Basil Bourque    schedule 18.03.2016
comment
К вашему сведению, неприятные старые классы даты и времени, такие как java.util.Date , java.util.Calendar и java.text.SimpleDateFormat теперь являются устаревшими, вытесненными java.time, встроенные в Java 8 и более поздние версии. См. учебник от Oracle.   -  person Basil Bourque    schedule 27.04.2018


Ответы (3)


Убедитесь, что вы используете правильную локаль. (Конструктор SimpleDateFormat(String) использует язык системы по умолчанию, который может отличаться от того, который вы хотите использовать.)

Это отлично работает на моей машине:

String input = "Aug 31 09:53:19 2011";
DateFormat df = new SimpleDateFormat("MMM dd HH:mm:ss yyyy", Locale.US);
System.out.println(df.parseObject(input));

(Например, при использовании Locale.FRENCH получается ParseException.)

person aioobe    schedule 31.08.2011
comment
Спасибо за совет! Этот баг меня тоже сбил с толку. - person Nik Reiman; 12.10.2011
comment
Это решает проблему, но почему? что меняется, когда локаль французская?? - person ByteMe; 03.04.2012
comment
название месяца - август вместо этого станет août - person gengkev; 16.11.2014

Сам формат подходит для ввода, который вы дали. Но вы можете получить эту ошибку, если ваш языковой стандарт по умолчанию установлен на что-то, где «Авг» не является допустимым сокращением названия месяца. Попробуйте использовать, например, Locale.US, и вы увидите, что это сработает:

DateFormat df = new SimpleDateFormat("MMM dd HH:mm:ss yyyy", Locale.US);
Date date = df.parse("Aug 31 09:53:19 2011");
person Jesper    schedule 31.08.2011
comment
У меня есть такая дата 2017-08-01T08:32:38.5544401-05:00, и это шаблон yyyy-MM-dd'T'HH:mm:ssz. и я получаю исключение. Пожалуйста помоги - person eC Droid; 01.08.2017
comment
@eCDroid ваша дата не соответствует шаблону; в строке даты есть доли секунды, которых нет в шаблоне. Используйте правильный шаблон. - person Jesper; 01.08.2017
comment
ты имеешь в виду сс.сссссссс? - person eC Droid; 01.08.2017
comment
@eCDroid Нет, это немного сложнее, потому что SimpleDateFormat не может работать с 7 цифрами долей секунды. См.: stackoverflow.com/questions/12000673/ - person Jesper; 01.08.2017
comment
Я попробовал это String actualFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSz"; и вот результат Orignal Date: 2017-08-01T08:50:36.8762641-05:00 Converted Time: 18:50:36 Это правильно? - person eC Droid; 01.08.2017
comment
@eCDroid Смотрите вопрос, на который я ссылался. - person Jesper; 01.08.2017
comment
Наблюдали ли вы в моем случае, что у меня есть секунды в десятичном значении без миллисекунд 08:50:36.8762641 эта цифра 36,8762641? - person eC Droid; 02.08.2017
comment
Я успешно добиваюсь этого, превращая шаблон в yyyy-MM-dd'T'HH:mm:ss - person eC Droid; 02.08.2017

tl;dr

  • Укажите Locale, чтобы определить человеческий язык и культурные нормы, используемые при переводе названия месяца.
  • Используйте современные классы java.time, а не проблемные устаревшие классы.

Надуманный пример:

LocalDateTime.parse(                                       // Parse input text as a `LocalDateTime` lacking any concept of time zone or offset-from-UTC.
    "Aug 31 09:53:19 2011" , 
    DateTimeFormatter.ofPattern( "MMM dd HH:mm:ss yyyy" )  // Specify formatting pattern to match input string.
                     .withLocale( Locale.US )              // The `Locale` determines the human language and cultural norms used in translating the input text.
)                                                          // Returns a `LocalDateTime` object.

Подробности

Два других ответа от aioobe и by Jesper верны: неявное использование Locale с человеческим языком, который не соответствует языку введенного вами текста.

Этот ответ объясняет новый способ выполнения работы. Кроме того, другие ответы не затрагивают важнейшую проблему часового пояса.

Java.время

Перенесемся через несколько лет после этой публикации, и теперь у нас есть новый пакет java.time, встроенный в Java 8 и более поздние версии. Эти новые классы заменяют старые классы java.util.Date/.Calendar и SimpleDateFormat. Эти старые классы оказались проблемными, запутанными и ошибочными.

Шаблон форматирования

Определите данные для анализа и их формат.

String input = "Aug 31 09:53:19 2011";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "MMM dd HH:mm:ss uuuu" );

Если не указано, DateTimeFormatter назначается Locale, который в настоящее время используется по умолчанию в JVM. Это значение по умолчанию может измениться в любой момент, даже во время выполнения (!). Поэтому всегда указывайте желаемое/ожидаемое Locale.

formatter = formatter.withLocale( Locale.US );  // Or Locale.UK, Locale.CANADA_FRENCH, etc.

Учитывая, что во входных данных отсутствует какая-либо информация о часовом поясе или смещении от-UTC, следует анализировать как LocalDateTime .

LocalDateTime ldt = LocalDateTime.parse( input , formatter );

Если из контекста вы знаете предполагаемое смещение от UTC или часовой пояс для этого значения даты и времени, назначьте его.

Если в формате UTC, используйте константу ZoneOffset.UTC, чтобы получить объект OffsetDateTime.

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );

Часовой пояс — это смещение относительно UTC плюс набор правил для обработки аномалий, таких как переход на летнее время (DST). Используйте правильные названия часовых поясов, а не аббревиатуры из 3-4 букв.

ZoneId zoneId_Montreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId_Montreal );

Укажите часовой пояс

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

Но обратите внимание, что часовой пояс также имеет решающее значение; другие ответы игнорировали эту проблему, тем самым неявно используя текущий часовой пояс JVM по умолчанию. Это не рекомендуется, так как это зависит от операционной системы хоста в качестве начального значения по умолчанию (поэтому может варьироваться), и, кроме того, любой код в любом потоке любого приложения в JVM может изменить текущий часовой пояс JVM по умолчанию во время время выполнения. Лучше указать желаемый/ожидаемый часовой пояс, чем неявно полагаться на значение по умолчанию.

Неизменяемые объекты

Обратите внимание на синтаксис. Эти классы должны быть неизменяемыми. Таким образом, вместо модификации (мутации) объекта новый новый объект создается на основе значений старого объекта. Это означает, что мы не затрагиваем DateTimeFormatter объект, определенный выше и хранящийся в переменной formatter (ссылка на объект). Мы создаем, используем и отбрасываем новый объект DateTimeFormatter (на самом деле, два новых объекта) в этой строке кода.

Справочник по методу

В документации предлагается альтернативный способ разбора строки — вызвать метод parse, в котором вы передаете ссылка на метод (новое в Java 8) из класса желаемого результата (как TemporalQuery): ZonedDateTime::from, LocalDateTime::from, LocalDate::from и т. д.

ZonedDateTime zdt = formatter.withZone( zoneId_Montreal ).withLocale( Locale.ENGLISH ).parse( input, ZonedDateTime :: from );

Для демонстрации давайте развернёмся и создадим строковое представление этого значения ZonedDateTime, но на французском языке Квебека.

String output = formatter.withLocale( Locale.CANADA_FRENCH ).format( zdt );

Еще лучше, давайте локализовать, а не жестко кодировать определенный формат.

String outputLocalized = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ).format( zdt );

Дамп на консоль.

System.out.println( "input: " + input );
System.out.println( "formatter: " + formatter );
System.out.println( "zdt: " + zdt );
System.out.println( "output: " + output );
System.out.println( "outputLocalized: " + outputLocalized );

Когда бег.

input: Aug 31 09:53:19 2011
formatter: Text(MonthOfYear,SHORT)' 'Value(DayOfMonth,2)' 'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)' 'Value(YearOfEra,4,19,EXCEEDS_PAD)
zdt: 2011-08-31T09:53:19-04:00[America/Montreal]
output: août 31 09:53:19 2011
outputLocalized: mercredi 31 août 2011 9 h 53 EDT

О java.time

java.time встроена в Java 8 и более поздние версии. Эти классы заменяют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и SimpleDateFormat.

Проект Joda-Time, теперь в режим обслуживания, советует перейти на java.time.

Чтобы узнать больше, см. Учебное пособие по Oracle. И поищите множество примеров и пояснений в Stack Overflow. Спецификация: JSR 310.

Вы можете обмениваться объектами java.time непосредственно с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

person Basil Bourque    schedule 17.08.2015