2017-12-27 20 views
4

Meine Anforderung ist zu validieren, dass ein Datum String im richtigen Format basierend auf einer Reihe von gültigen Formaten angegeben ist.Java 8 DateTimeFormatterBuilder(). AppendOptional funktioniert nicht

Gültige Formate:

MM/dd/yy 
MM/dd/yyyy 

habe ich ein einfaches Testverfahren, die die Java 8 DateTimeFormatterBuilder verwendet eine flexible Formatierungsprogramm zu erstellen, die mehrere optionale Formate unterstützt. Hier ist der Code:

public static void test() { 
    DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
      .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy")) 
      .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy")) 
      .toFormatter(); 

    String dateString = "10/30/2017"; 

    try { 
     LocalDate.parse(dateString, formatter); 
     System.out.println(dateString + " has a valid date format"); 
    } catch (Exception e) { 
     System.out.println(dateString + " has an invalid date format"); 
    } 
} 

Als ich das laufen, hier ist der Ausgang

10/30/2017 has an invalid date format 

Wie Sie im Code zu sehen, die die gültigen Datumsformate MM/TT/JJ und MM/dd/JJJJ. Meine Erwartung war, dass das Datum 30.10.2017 gültig sein sollte, da es MM/TT/JJJJ entspricht. Der 30.10.2017 wird jedoch als ungültig gemeldet.

Was läuft falsch? Warum funktioniert das nicht?

Ich habe auch versucht

.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy[yy]")) 

anstelle von

.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy")) 
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy")) 

hatte aber immer noch das gleiche Problem.

Dieser Code läuft wie erwartet, wenn ich benutze:

String dateString = "10/30/17"; 

anstelle von

String dateString = "10/30/2017"; 

Ich habe 2 Fragen

  1. Was falsch ist hier los? Warum funktioniert es nicht für "30.10.2017"?

  2. Wie kann mit Java 8 ein flexibler Date-Formatierer (ein Formatierer, der mehrere optionale Formate unterstützt) korrekt erstellt werden? Ich kenne die Verwendung von [], um optionale Abschnitte in der Musterzeichenfolge selbst zu erstellen. Ich bin auf der Suche nach etwas ähnlich zu dem, was ich versuche, ([] in der Musterkette zu vermeiden und mit separaten optionalen Klauseln für jedes einzelnes Formatstring)

+0

Das ist nicht wie 'appendOptional()' soll verwendet werden (bitte siehe meine Antwort unten). Um ein anderes Format für das Datum zu ermöglichen, erstellen Sie am einfachsten eine Formatierungsliste, aber ich bin mir nicht sicher, ob dies der beste Weg ist. –

Antwort

3

Der Formatierer nicht wie erwartet funktioniert, die optionaler Bestandteil bedeutet

  • wenn es nichts extra an dem ersten Muster (zB „MM/tT/JJ“), das ist in Ordnung,
  • , wenn es etwas Besonderes ist, braucht es die zweite anzupassen Muster (e.g, "MM/tt /")

es ein wenig klarer zu machen, versuchen Sie den folgenden Beispielcode ausführen es besser zu verstehen:

DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
      .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy")) 
      .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy")) 
      .toFormatter(); 

    String[] dateStrings = { 
      "10/30/17",   // valid 
      "10/30/2017",   // invalid 
      "10/30/1710/30/2017", // valid 
      "10/30/201710/30/17" // invalid 
    }; 

    for (String dateString : dateStrings) { 
     try { 
      LocalDate.parse(dateString, formatter); 
      System.out.println(dateString + " has a valid date format"); 
     } catch (Exception e) { 
      System.err.println(dateString + " has an invalid date format"); 
     } 
    } 

==

10/30/17 has a valid date format 
10/30/1710/30/2017 has a valid date format 
10/30/2017 has an invalid date format 
10/30/201710/30/17 has an invalid date format 

==

Dies ist nur eine einfache Lösung, Wenn die Leistung von Interesse ist, sollte die Validierung durch Abfangen der Analyse Ausnahme sein t-Resort

  • Sie die Zeichenfolge zuerst durch Länge oder Regex überprüfen kann, bevor das Datum Zeichenfolge tun Parsen
  • Sie auch den Strom mit einem Verfahren ersetzen kann eine einfache for-Schleife enthält, usw.

    String[] patterns = { "MM/dd/yy", "MM/dd/yyyy" }; 
    Map<String, DateTimeFormatter> formatters = Stream.of(patterns).collect(Collectors.toMap(
         pattern -> pattern, 
         pattern -> new DateTimeFormatterBuilder().appendOptional(DateTimeFormatter.ofPattern(pattern)).toFormatter() 
    )); 
    
    String dateString = "10/30/17"; 
    boolean valid = formatters.entrySet().stream().anyMatch(entry -> { 
        // relying on catching parsing exception will have serious expense on performance 
        // a simple check will already improve a lot 
        if (dateString.length() == entry.getKey().length()) { 
         try { 
          LocalDate.parse(dateString, entry.getValue()); 
          return true; 
         } 
         catch (DateTimeParseException e) { 
          // ignore or log it 
         } 
        } 
        return false; 
    }); 
    
+0

Sinn macht und ich werde meine Antwort löschen, wenn ich als nächstes auf einem Laptop bin - aber die Antwort wäre besser, wenn Sie den Code für das zur Verfügung stellen, was das OP * verwenden sollte. –

0

Die Methode des Herstellers appendValueReduced() wurde entwickelt, um diesen Fall zu behandeln.

Wenn Sie einen vollständigen Wert für ein Feld analysieren, behandelt der Formatierer ihn als absoluten Wert.

Wenn Sie einen Teilwert für ein Feld analysieren, interpretiert der Formatierer ihn relativ zu einer von Ihnen angegebenen Basis. Wenn Sie beispielsweise zweistellige Jahre als zwischen 1970 und 2069 interpretieren möchten, können Sie 1970 als Basis angeben. Hier ist eine Illustration:

LocalDate century = LocalDate.ofEpochDay(0); /* Beginning Jan. 1, 1970 */ 
    DateTimeFormatter f = new DateTimeFormatterBuilder() 
      .append(DateTimeFormatter.ofPattern("MM/dd/")) 
      .appendValueReduced(ChronoField.YEAR, 2, 4, century) 
      .toFormatter(); 
    System.out.println(LocalDate.parse("10/30/2017", f)); /* 2017-10-30 */ 
    System.out.println(LocalDate.parse("10/30/17", f)); /* 2017-10-30 */ 
    System.out.println(LocalDate.parse("12/28/1969", f)); /* 1969-12-28 */ 
    System.out.println(LocalDate.parse("12/28/69", f)); /* 2069-12-28 */ 
Verwandte Themen