java.time
Давайте посмотрим, может ли помочь java.time framework.
О java.time
Инфраструктура java.time , встроенная в Java 8 и более поздние версии, заменяет проблемные старые классы java.util.Date/.Calendar. Новые классы вдохновлены очень успешным фреймворком Joda-Time , задуманным как его преемник, похожим по концепции, но с новой архитектурой. Определяется JSR 310 . Расширена проектом ThreeTen-Extra . См. Учебник .
LocalDate
В отличие от старых классов, java.time предлагает класс LocalDate
для представления значения только для даты, без времени суток и часового пояса.
Французские сокращения
Посмотрите, что ожидают форматеры в java.time для сокращенных названий месяцев в ru Français .
Мы можем перебрать перечисление Month
, чтобы получить список месяцев. Это перечисление предлагает метод getDisplayName
для генерации локализованного названия месяца. Этот код демонстрирует, что метод производит тот же вывод, что и форматтер java.time.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "dd-MMM-yyyy" ).withLocale ( Locale.FRENCH );
for ( Month month : Month.values () ) {
LocalDate localDate = LocalDate.of ( 2015 , month.getValue () , 1 );
String output = formatter.format ( localDate );
String displayName = month.getDisplayName ( TextStyle.SHORT , Locale.FRENCH );
System.out.println ( "output: " + output + " | displayName: " + displayName );// System.out.println ( "input: " + input + " → " + localDate + " → " + output );
}
output: 01-janv.-2015 | displayName: janv.
output: 01-févr.-2015 | displayName: févr.
output: 01-mars-2015 | displayName: mars
output: 01-avr.-2015 | displayName: avr.
output: 01-mai-2015 | displayName: mai
output: 01-juin-2015 | displayName: juin
output: 01-juil.-2015 | displayName: juil.
output: 01-août-2015 | displayName: août
output: 01-sept.-2015 | displayName: sept.
output: 01-oct.-2015 | displayName: oct.
output: 01-nov.-2015 | displayName: nov.
output: 01-déc.-2015 | displayName: déc.
Мы находим смесь из 3 и 4 букв написания. Длинные имена сокращаются до четырех символов плюс точка ( FULL STOP ). Четыре месяца имеют достаточно короткие названия, чтобы использовать их без сокращений: mars
, mai
, juin
, août
.
Итак, как обсуждалось в других Ответах, простого решения нет.
Исправить источник данных
Мое первое предложение - исправить ваш источник данных. Этот источник, по-видимому, не соответствует надлежащим французским правилам сокращения. Йельский согласен с пониманием французского языка в Java 8. Кстати, если вы исправляете ваш источник данных, я настоятельно рекомендую использовать четырехзначные годы, так как два приводят к бесконечной путанице и неопределенности.
Исправить ввод
Конечно, источник может быть вне вашего контроля / влияния. В этом случае, как и в случае с другими Ответами, вам может потребоваться сделать грубую замену, а не пытаться применить какой-либо ум. С другой стороны, если единственная проблема с вашим вводом - просто пропустить точку (FULL STOP), то вы можете использовать программный код, используя перечисление Month
вместо жесткого кодирования неправильных значений.
Я бы сделал начальную попытку разбора. Ловушка для DateTimeParseException
, прежде чем пытаться исправить. Если выдается исключение, исправьте ввод.
Чтобы исправить ввод, попробуйте каждый месяц года, зацикливая возможный набор экземпляров enum. За каждый месяц получайте его сокращенное название. Удалите точку (FULL STOP) из этой аббревиатуры, чтобы она соответствовала тому, что мы подозреваем, является нашим неправильным входящим значением. Проверьте, действительно ли это соответствует входным данным. Если нет, переходите к следующему месяцу.
Когда мы получим совпадение, исправьте ввод, чтобы он был правильно сокращен для правил Locale (французские правила в нашем случае). Затем проанализируйте фиксированный ввод. Это будет наша вторая попытка разбора, так как мы сделали первоначальную попытку наверху. Если вторая попытка не удалась, что-то не так, как отмечено в FIXME:
, показанном здесь. Но обычно эта вторая попытка разбора будет успешной, и мы можем выйти из цикла for
перечисления Month
.
Наконец, вы можете проверить успешность, проверив, является ли результат все еще фиктивным значением, установленным изначально (LocalDate.MIN
).
String input = "09-oct-08"; // Last two digits are Year.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "dd-MMM-yy" ).withLocale ( Locale.FRENCH );
LocalDate localDate = LocalDate.MIN; // Some folks prefer a bogus default value as a success/failure flag rather than using a NULL.
try {
localDate = LocalDate.parse ( input , formatter );
} catch ( DateTimeParseException e ) {
// Look for any month name abbreviation improperly missing the period (FULL STOP).
for ( Month month : Month.values () ) {
String abbreviation = month.getDisplayName ( TextStyle.SHORT , Locale.FRENCH );
String abbreviationWithoutFullStop = abbreviation.replace ( "." , "" ); // Get short abbreviation, but drop any period (FULL STOP).
String proper = "-" + abbreviation + "-";
String improper = "-" + abbreviationWithoutFullStop + "-";
if ( input.contains ( improper ) ) {
String inputFixed = input.replace ( improper , proper );
try {
localDate = LocalDate.parse ( inputFixed , formatter );
} catch ( DateTimeParseException e2 ) {
// FIXME: Handle this error. We expected this second parse attempt to succeed.
}
break; // Bail-out of the loop as we got a hit, matching input with a particular improper value.
}
}
}
Boolean success = ! ( localDate.equals ( LocalDate.MIN ) );
String formatted = formatter.format ( localDate );;
String outputImproper = formatted.replace ( "." , "" ); // Drop any period (FULL STOP).
Дамп на консоль.
System.out.println ( "success: " + success + ". input: " + input + " → localDate: " + localDate + " → formatted: " + formatted + " → outputImproper: " + outputImproper );
успех: правда. вход: 09-окт-08 → localDate: 2008-10-09 → форматированный: 09-окт.-08 → outputImproper: 09-окт-08