web-dev-qa-db-fra.com

Java 8 Date équivalente à DateTimeFormatterBuilder de Joda avec plusieurs formats d’analyseur?

J'ai actuellement un analyseur de date Joda qui utilise DateTimeFormatterBuilder avec une demi-douzaine de formats de date différents que je pourrais recevoir.

Je migre vers les routines Date de Java 8 et je ne vois pas d'équivalent.

Comment puis-je faire quelque chose comme ça avec 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();
21
Todd

Il n’existe aucune installation directe pour cela, mais vous pouvez utiliser des sections facultatives. Les sections facultatives sont placées entre crochets []. Cela permet d’analyser toute la section de la chaîne.

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

Ce formateur définit 3 grandes sections facultatives pour les trois modèles principaux que vous avez. Chacun d'entre eux est à l'intérieur de sa propre section facultative.

Code de démonstration de travail:

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));
}
21
Tunaki

Comme alternative à Tunaki, vous pouvez également utiliser DateTimeFormatterBuilder:

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

Itération sur la solution de @ Tunaki, à l'aide de flux, lorsque le code doit accepter différents modèles de manière configurable: 

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

Dans ce cas, je me fiche de la partie du combinateur du réducteur, mais j'en ai besoin dans la signature pour que le combineur soit correct.

Ce code serait pratiquement équivalent à si les modèles ci-dessus (yyyy/MM/dd HH:mm:ss.SSSSSS, yyyy-MM-dd HH:mm:ss[.SSS], ddMMMyyyy:HH:mm:ss.SSS[ Z]) étaient envoyés au flux:

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();
5
Brice

Voici ce que j'ai finalement trouvé. Il gère trois formats principaux différents, chacun avec de nombreuses différences de formatage mineures (telles que permettre soit les délimiteurs, soit les /) ainsi que le nombre variable de microsecondes (de 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();
0
Todd

Sur la base de la réponse de @Brice, j'ai écrit la méthode suivante, qui a fonctionné le mieux pour moi.

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;
}

Faites attention à l'ordre des formateurs optionnels ajoutés au dernier. L'ordre inverse ne fonctionne que pour les dates du type "aaaa". L'ordre proposé me permet d'analyser tous les éléments suivants:

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

Juste pour ajouter au cas où quelqu'un aurait un cas d'utilisation similaire. 

0
Ira Re