Подтвердить что ты не робот

Java 8 Дата, эквивалентная Joda DateTimeFormatterBuilder с несколькими форматами парсера?

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

Я переношусь на Java 8 Date и не вижу эквивалента.

Как я могу сделать что-то подобное с помощью Java 8 Dates?

DateTimeParser[] parsers = { 
    DateTimeFormat.forPattern( "yyyy/MM/dd HH:mm:ss.SSSSSS" ).getParser() ,
    DateTimeFormat.forPattern( "yyyy-MM-dd HH:mm:ss" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSS Z" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSS" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSSSSS" ).getParser() ,
    DateTimeFormat.forPattern( "yyyy-MM-dd HH:mm:ss.SSS" ).getParser() 
};

DateTimeFormatter dateTimeFormatterInput = new DateTimeFormatterBuilder()
     .append( null, parsers ).toFormatter();
4b9b3361

Ответ 1

Нет прямого средства для этого, но вы можете использовать необязательные разделы. Дополнительные секции заключены в квадратные скобки []. Это позволяет не анализировать весь раздел строки.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
    + "[yyyy/MM/dd HH:mm:ss.SSSSSS]"
    + "[yyyy-MM-dd HH:mm:ss[.SSS]]"
    + "[ddMMMyyyy:HH:mm:ss.SSS[ Z]]"
);

Этот форматтер определяет 3 больших необязательных раздела для трех основных шаблонов, которые у вас есть. Каждый из них находится внутри своего необязательного раздела.

Рабочий демонстрационный код:

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
        + "[yyyy/MM/dd HH:mm:ss.SSSSSS]"
        + "[yyyy-MM-dd HH:mm:ss[.SSS]]"
        + "[ddMMMyyyy:HH:mm:ss.SSS[ Z]]"
    , Locale.ENGLISH);
    System.out.println(LocalDateTime.parse("2016/03/23 22:00:00.256145", formatter));
    System.out.println(LocalDateTime.parse("2016-03-23 22:00:00", formatter));
    System.out.println(LocalDateTime.parse("2016-03-23 22:00:00.123", formatter));
    System.out.println(LocalDateTime.parse("23Mar2016:22:00:00.123", formatter));
    System.out.println(LocalDateTime.parse("23Mar2016:22:00:00.123 -0800", formatter));
}

Ответ 2

В качестве альтернативного ответа на Tunaki вы также можете использовать DateTimeFormatterBuilder:

DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder()
  .appendPattern("[yyyy]")
  .appendPattern("[M/d/yyyy]")
  .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
  .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
  .toFormatter()

Ответ 3

Итерация по решению @Tunaki с использованием потоков, когда код должен принимать различные шаблоны настраиваемым образом:

DateTimeFormatter dateTimeFormatter = dateFormats.stream()
        .map(DateTimeFormatter::ofPattern)
        .reduce(new DateTimeFormatterBuilder(), 
                DateTimeFormatterBuilder::appendOptional, 
                (f1, f2) -> f1.append(f2.toFormatter()))
        .toFormatter();

В этом случае меня не волнует комбинаторная часть редуктора, но мне нужна его в сигнатуре, поэтому я правильно сделал комбайнер.

Этот код был бы практически эквивалентен тому, если бы приведенные выше шаблоны (yyyy/MM/dd HH:mm:ss.SSSSSS, yyyy-MM-dd HH:mm:ss[.SSS], ddMMMyyyy:HH:mm:ss.SSS[ Z]) были отправлены в поток:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSSSS")
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]"
    .appendOptional(DateTimeFormatter.ofPattern("ddMMMyyyy:HH:mm:ss.SSS[ Z]")
    .toFormatter();

Ответ 4

Основываясь на ответе @Brice, я написал следующий метод, который работал лучше всего для меня

private LocalDate parseDate(String date) {
    DateTimeFormatter yearFormat = new DateTimeFormatterBuilder()
            .appendPattern("yyyy")
            .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
            .toFormatter();
    DateTimeFormatter yearAndMonthFormat = new DateTimeFormatterBuilder()
            .appendPattern("yyyy-MM")
            .parseDefaulting(ChronoField.DAY_OF_MONTH,1)
            .toFormatter();

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendOptional(DateTimeFormatter.ISO_DATE)
            .appendOptional(yearAndMonthFormat)
            .appendOptional(yearFormat)
            .toFormatter();

    LocalDate result = LocalDate.parse(date, formatter);

    return result;
}

Обратите внимание на порядок добавления дополнительных форматировщиков к последнему. Обратный порядок будет работать только для дат типа "гггг". Предложенный порядок позволяет мне разобрать все следующее:

  • 2014
  • 2014-10
  • 2014-03-15

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

Ответ 5

Вот что я в итоге придумал. Он обрабатывает три различных основных формата, каждый с несколькими незначительными различиями в форматировании (например, допускает разделители - или /), а также обрабатывает переменное число микросекунд (от 0 до 6):

private static final DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendPattern( "[ddMMMyyyy:HH:mm:ss" )
        .optionalStart()
        .appendFraction( ChronoField.MICRO_OF_SECOND , 1 , 6 , true )
        .optionalEnd()
        .appendPattern( "[ ][Z][X]]" )
        .appendPattern( "[yyyy[-][/]MM[-][/]dd['T'][ ]HH:mm[:][.]ss" )
        .optionalStart()
        .appendFraction( ChronoField.MICRO_OF_SECOND , 1 , 6 , true )
        .optionalEnd()
        .appendPattern( "[Z][X]]" )
        .appendPattern( "[EEE, dd MMM yyyy HH:mm:ss zzz]" )
        .toFormatter();