2016-07-19 22 views
4

wir eine REST-Webservice laufen, die verschiedene Daten verbraucht, meine aktuellen Ausgabe gehört zu einem Zeitpunkt, als String empfangen und analysiert durch eine java.text.SimpleDateFormat (Java 8):„Reverse“ falsch analysiert Datum

Wir habe sehr viele (> 50k) "falsch" formatierte Strings erhalten, die sowieso vom SimpleDateFormat analysiert wurden.

Das SimpleDateFormat ist mit dem Muster "yyyy-MM-dd" konfiguriert. Wir erhielten Strings umgekehrt "dd-MM-yyyy".

Zum Beispiel wurde die Zeichenfolge "07-07-1950" auf das Datum "0012-10-31" geparst (ab Juli in Jahr 7, 1950 Tage hinzugefügt).

Wir haben die Implementierung repariert, daher werden diese Strings nun wie erwartet analysiert. Aber wir haben alle korrupten Daten im System. Die letzte Frage lautet jetzt:

Gibt es eine Möglichkeit, vom Datum "0012-10-31" auf mögliche Originaleingänge (zB "07-07-1950", "07-06-1980" und vielleicht mehr) zu schließen ...)?

Mit freundlichen Grüßen

+0

By the way, die störenden alten Datum-Zeit-Klassen wie [ 'java.util.Date'] (https://docs.oracle.com/javase/ (Java.util.Calendar) (https://docs.oracle.com/javase/9/docs/api/java/util/Calenda) r.html) und 'java.text.SimpleDateFormat' sind nun [legacy] (https://en.wikipedia.org/wiki/Legacy_system), ersetzt durch [* java.time *] (https: // docs .oracle.com/javase/9/docs/api/java/time/package-summary.html) Klassen, die in Java 8 und Java 9 integriert sind. Siehe [* Tutorial * by Oracle] (https://docs.oracle.com /javase/tutorial/datetime/TOC.html). –

Antwort

1

Aufbauend auf Martin Ackermann's answer:

Zunächst einmal vereinfacht ich den Code ein wenig.

Die einfache Korrektur der ungültigen Daten hängt davon ab, in welchem ​​Bereich gültige Daten liegen.
Zum Beispiel, wenn max=2016-12-31 dann die folgende Tabelle zeigt die Anzahl der eindeutigen Daten, die auf min je fixierbar/mehrdeutig sind

min   fixable ambiguous 
----------------------------- 
1990-01-01 9862 0 
1980-01-01 8827 2344 
1970-01-01 5331 5918 
1960-01-01 1832 9494 
1950-01-01 408  10950 
1940-01-01 314  11054 
1930-01-01 218  11160 
1920-01-01 165  11223 
1910-01-01 135  11263 
1900-01-01 105  11303 

Mehrdeutige Treffer für ungültige Datumsangaben bei etwa 30 Jahren Intervallen auftreten, so dass, wenn die tatsächlichen Daten in einem fallen Zeitraum von 30 Jahren, dann sind Sie im Glück

LocalDate max = LocalDate.of(2016, Month.DECEMBER, 31); 
    LocalDate min = max.minusYears(30); 
    Map<String, Set<LocalDate>> invalidDateMapping = createDateMapping(min, max); 
    long reversibleCount = invalidDateMapping.entrySet().stream().filter(e -> e.getValue().size() == 1).count(); // 10859 
    long ambiguousCount = invalidDateMapping.size() - reversibleCount; // 50 
0

Ich glaube nicht, Sie in der Lage sein werden, das ursprüngliche Datum des beschädigten Eingangs, um herauszufinden, aber Sie sollten alle beschädigten Termine zu finden sein können und vielleicht einen Weg finden, wieder zu konsumieren, dass Daten. Dies liegt daran, dass jedes Datum um eine unbekannte Anzahl von Tagen geändert wurde. Wenn Sie diesen Vorgang rückgängig machen möchten, müssen Sie wissen, die Anzahl der Tage oder das Startdatum, und es sieht so aus, als ob Sie das hier nicht haben.

Das gesagt, es wird eigentlich ziemlich einfach sein, alle Daten einzugrenzen, die beschädigt waren.

Der größte Wert, den Sie für einen Monat erhalten, sollte 12 sein. Das bedeutet, dass das neueste "Jahr" für Ihre beschädigten Daten das Jahr 12 sein wird. Wenn Ihre Daten bis jetzt laufen, das größte Jahr (welches wurde fälschlicherweise als Tage geparst) wird 2016 sein, was sich in ca. 5,5 Jahre umwandeln würde. So sind alle Daten mit Jahren unter 18 oder 19 beschädigt, und Sie sollten in der Lage sein, sie mindestens zu entfernen.

Der einzige Randfall hier ist, wenn Sie Daten haben, die Jahre haben, die gültig in den frühen Teenagerjahren landen werden. Wenn das der Fall ist, müsstest du diese mit der Hand durchgehen. Aber das scheint unwahrscheinlich.

-1

Haben Sie versucht SimpleLenient zu falsch

package test;   

    import java.text.ParseException;    
    import java.text.SimpleDateFormat;   
    import java.util.Date;   

    public class Test {   

     public static void main(String[] args) throws ParseException {   
      SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");   
      SimpleDateFormat dateFormat2 = new SimpleDateFormat("dd-MM-yyyy");   
      dateFormat1.setLenient(false);   
      dateFormat2.setLenient(false);   
      Date d = null;   
      String invalidDate = "07-06-1980";   
     try {   
      d = dateFormat1.parse(invalidDate);   
     } catch (Exception e) {   
      System.out.println("reversed date " + invalidDate);   
      d = dateFormat2.parse(invalidDate);   
     }   

     System.out.println(parsed date " + dateFormat1.format(d));   
    }   
}   

umgekehrt Datum 07-06-1980

analysiert Datumseinstellung 1980-06-07

+1

Die Frage war nicht, wie man das Datum richtig analysiert oder wie man falsches Parsing vermeidet - es ging um Schlussfolgerungen von bereits falsch geparsten Daten zu ursprünglichen Eingaben. –

2

Ich fand einen Weg t o mögliche Eingaben finden:

Ich kann Kalender verwenden, um mögliche Daten zu durchlaufen, die Daten auf die "Wron" -Weise zu analysieren und eine Karte mit diesen Informationen zu erstellen.

public static Map<String, Collection<String>> createDateMapping() throws ParseException 
{ 
    final DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd"); 
    final DateFormat wrongFormat = new SimpleDateFormat("dd-MM-yyyy"); 

    //starting today 
    final Calendar cal = Calendar.getInstance(); 

    final Map<String, Collection<String>> inputMappings = new HashMap<>(); 

    //rolling down to year zero is quite time consuming, back to year 1899 should be enough... 
    while (cal.get(Calendar.YEAR) > 1899) 
    { 
     //creating the "wrong" date string 
     final String formattedDate = wrongFormat.format(cal.getTime()); 
     final String key = targetFormat.format(targetFormat.parse(formattedDate)); 

     if (!inputMappings.containsKey(key)) 
     { 
      inputMappings.put(key, new ArrayList<>()); 
     } 

     inputMappings.get(key).add(targetFormat.format(cal.getTime())); 

     //roll calendar to previous day 
     cal.roll(Calendar.DAY_OF_YEAR, false); 

     if (cal.get(Calendar.DAY_OF_YEAR) == 1) 
     { 
      //roll down the year manually, since it is not rolled down automatically 
      cal.roll(Calendar.DAY_OF_YEAR, false); 

      //roll down the day again, to start at the last day of the year again 
      cal.roll(Calendar.YEAR, false); 
     } 
    } 

    return inputMappings; 
} 

durch die Anwendung dieser Methode ich kann:

final Map<String, Collection<String>> dateMapping = createDateMapping(); 

System.out.println(dateMapping.get("0012-10-31"));//[2011-05-07, 1980-06-07, 1950-07-07, 1919-08-07] 

Es wird das Problem nicht vollständig lösen, aber es ist zumindest ein guter Ausgangspunkt - hoffentlich gibt es einige Termine mit expliziten Ergebnissen.