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();
Ответы
Ответ 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;
}
Обратите внимание на порядок добавления дополнительных форматировщиков к последнему. Обратный порядок будет работать только для дат типа "гггг". Предложенный порядок позволяет мне разобрать все следующее:
Просто добавив для случая, кто-то будет иметь аналогичный вариант использования.
Ответ 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();