Разбор даты с коротким месяцем без точки

У меня есть строка, представляющая дату в французском языке: 09-окт-08:

Мне нужно разобрать, что String поэтому я придумал этот SimpleDateFormat:

String format2 = "dd-MMM-yy";

Но у меня есть проблема с частью месяца, которая, кажется, ожидается с конечной точкой:

df2.format(new Date());

дает мне:

 28-oct.-09

Что для меня лучше всего понять SimpleDateFormat ( "09-окт-08" )?

Полный код:

String format2 = "dd-MMM-yy"; 
DateFormat df2 = new SimpleDateFormat(format2,Locale.FRENCH); 
date = df2.parse("09-oct-08"); 

Это дает мне: java.text.ParseException: Непревзойденная дата: "09-окт-08"

И если я затем попытаюсь войти в журнал:

df2.format(new Date()); 

Я получаю: 28-окт.-09

Ответ 1

Это работает:

    DateFormatSymbols dfsFr = new DateFormatSymbols(Locale.FRENCH);
    String[] oldMonths = dfsFr.getShortMonths();
    String[] newMonths = new String[oldMonths.length];
    for (int i = 0, len = oldMonths.length; i < len; ++ i) {
        String oldMonth = oldMonths[i];

        if (oldMonth.endsWith(".")) {
            newMonths[i] = oldMonth.substring(0, oldMonths[i].length() - 1);
        } else {
            newMonths[i] = oldMonth;
        }
    }
    dfsFr.setShortMonths(newMonths);
    DateFormat dfFr = new SimpleDateFormat(
        "dd-MMM-yy", dfsFr);

    // English date parser for creating some test data.
    DateFormat dfEn = new SimpleDateFormat(
        "dd-MMM-yy", Locale.ENGLISH);
    System.out.println(dfFr.format(dfEn.parse("10-Oct-09")));
    System.out.println(dfFr.format(dfEn.parse("10-May-09")));
    System.out.println(dfFr.format(dfEn.parse("10-Feb-09")));

Edit: Похоже, что Shadow избили меня.

Ответ 2

Вы можете просто удалить ".":

df2.format(new Date()).replaceAll("\\.", ""));

Изменить, в отношении ответа лимона:

Кажется, что проблема связана с форматированием при использовании языка Locale French. Таким образом, я предлагаю вам просто использовать удаление ., как я объяснил.

В самом деле, следующий код:

    String format2 = "dd-MMM-yy";
    Date date = Calendar.getInstance().getTime();
    SimpleDateFormat sdf = new SimpleDateFormat(format2, Locale.FRENCH);
    System.out.println(sdf.format(date));
    sdf = new SimpleDateFormat(format2, Locale.ENGLISH);
    System.out.println(sdf.format(date));

отображает следующий вывод:

28-oct.-09
28-Oct-09

Изменить снова

Хорошо, у меня есть твоя проблема прямо сейчас.

Я действительно не знаю, как вы можете решить эту проблему, не обработав первую строку. Идея состоит в том, чтобы заменить месяц в исходной строке на полный месяц:

        String[] givenMonths = { "jan", "fév", "mars", "avr.", "mai", "juin", "juil", "août", "sept", "oct", "nov", "déc" };
        String[] realMonths = { "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc." };
        String original = "09-oct-08";
        for (int i = 0; i < givenMonths.length; i++) {
            original = original.replaceAll(givenMonths[i], realMonths[i]);
        }
        String format2 = "dd-MMM-yy";
        DateFormat df2 = new SimpleDateFormat(format2, Locale.FRENCH);
        Date date = df2.parse(original);
        System.out.println("--> " + date);

Я согласен, это ужасно, но я не вижу другого решения, если вы используете классы SimpleDateFormat и Date.

Другим решением является использование библиотеки времени и времени реального вместо исходных JDK файлов, таких как Joda Time.

Ответ 3

String format2 = "dd-MMM-yy";
Date date = Calendar.getInstance().getTime();
SimpleDateFormat sdf = new SimpleDateFormat(format2);
System.out.println(sdf.format(date));

Выходы 28-Oct-09

Я не вижу точек сэр. Вы пытались перепроверить свои отпечатки? Возможно, вы случайно разместили . рядом с вашим MMM?


Вы получаете java.text.ParseException: Unparseable date: "09-oct-08", так как "09-oct-08" не соответствует форматированию Locale.FRENCH либо использовать стандартную локаль (думаю, я думаю), либо добавить . рядом с вашим oct

Ответ 4

Хорошо, тогда попробуйте "грубая сила":)

DateFormatSymbols dfs = new DateFormatSymbols(Locale.FRENCH);
String[] months = new String[13]
<fill with correct month names or just replace these month, that are not fully correct>
dfs.setMonths(months);
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yy", dfs);
Date nweDate = sdf.parse("09-fév-08");

Ответ 5

java.time

Давайте посмотрим, может ли помочь инфраструктура java.time.

О java.time

Структура java.time, встроенная в Java 8 и более поздние версии, вытесняет неприятные старые классы java.util.Date/.Calendar. Новые классы вдохновлены очень успешной структурой Joda-Time, предназначенной в качестве ее преемника, схожей по концепции, но с повторной архитектурой. Определено JSR 310. Расширяется проектом ThreeTen-Extra. См. Tutorial.

LocalDate

В отличие от старых классов, java.time предлагает класс LocalDate для представления значения только для даты, без времени, день или часовой пояс.

Французские сокращения

Взгляните на то, что formatters в java.time ожидают сокращения сокращенных имен в en 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.

Итак, как обсуждалось в других ответах, нет простого решения.

Исправить источник данных

Мое первое предложение - исправить ваш источник данных. Похоже, что этот источник не соблюдает правильные французские правила аббревиатуры. Yale согласуется с пониманием французского языка Java 8s. Кстати, если вы исправляете свой источник данных, я настоятельно рекомендую использовать четырехзначные годы, так как два не приводят к путанице и двусмысленности.

Исправить вход

Конечно, источник может оказаться вне вашего контроля/влияния. В этом случае, как и в случае с другими ответами, вам может потребоваться заменить грубую силу, а не пытаться любой ум. С другой стороны, если единственная проблема с вашим входом - просто пропустить период (FULL STOP), тогда вы можете мягко программировать с использованием перечня Month, а не жесткого кода, неправильных значений.

Я бы сделал начальную попытку синтаксического анализа. Ловушка для DateTimeParseException перед попыткой исправления. Если исключение выбрано, тогда исправьте ввод.

Чтобы исправить ввод, попробуйте каждый месяц года, зациклив возможный набор экземпляров enum. За каждый месяц получите свое сокращенное имя. Удалите период (FULL STOP) из этой аббревиатуры, чтобы соответствовать тому, что мы подозреваем, является нашей неправильной входящей стоимостью. Проверьте, действительно ли это соответствует вводу. Если нет, перейдите к следующему месяцу.

Когда мы получим соответствие, исправьте ввод, который будет правильно сокращен для правил Locales (французские правила в нашем случае). Затем проанализируйте фиксированный вход. Это будет наша вторая попытка синтаксического анализа, поскольку мы сделали начальную попытку вверх. Если эта вторая попытка потерпит неудачу, что-то очень не так, как отмечено в 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 );

success: true. вход: 09-окт-08 → localDate: 2008-10-09 → отформатирован: 09-окт.-08 → outputImproper: 09-окт-08

Ответ 6

У меня была такая же проблема (французский и дополнительные точки), и я считаю, что правильный способ решить эту проблему - глобально переписать французский язык так:

import moment from 'moment';
moment.locale('fr', { monthsShort: 'janv_févr_mars_avr_mai_juin_juil_août_sept_oct_nov_déc'.split('_') });

Оригинальный французский объект monthsShort имеет такие точки, как janv._févr._mars_avr._..., поэтому мы просто удаляем их.

Здесь ссылка на документы, где вы можете проверить, что можно перезаписать.

Обратите внимание, что нам не нужно передавать полный объект локали, если мы просто хотим перезаписать, например.: monthsShort.