2016-07-19 51 views
4

我們運行一個REST Web服務消耗不同的數據,我現在的問題是屬於一個日期,收到的字符串並解析由java.text.SimpleDateFormat中(Java 8):「反向」錯誤解析日期

我們收到了很多(> 50k)錯誤的格式化字符串,而這些字符串都是由SimpleDateFormat解析的。

SimpleDateFormat的被配置成與圖案 「YYYY-MM-DD」。 我們以「dd-MM-yyyy」的方式接收了字符串。

例如字符串「1950年7月7日」被解析到日期「0012-10-31」(從七月在今年7起,由1950年天)。

我們修復了這個實現,所以這些字符串現在按預期進行了解析。但是我們有系統中的所有腐敗日期。最後一個問題是現在:

有沒有辦法從日期「0012-10-31」儘可能原始輸入(例如「1950年7月7日」結束,「1980年7月6日」,也許更多...)?

問候

+0

順便說一句,麻煩的舊日期時間類,如['java.util.Date'](https://docs.oracle.com/javase/ 9/docs/api/java/util/Date.html),['java.util.Calendar'](https://docs.oracle.com/javase/9​​/docs/api/java/util/Calenda r.html)和'java.text.SimpleDateFormat'現在是[legacy](https://en.wikipedia.org/wiki/Legacy_system),由[* java.time *]代替(https:// docs .oracle.com/javase/9/docs/api/java/time/package-summary.html)內置於Java 8和Java 9的類。請參閱[* Oracle *教程](https://docs.oracle.com /javase/tutorial/datetime/TOC.html)。 –

回答

1

大廈Martin Ackermann's answer

首先,我簡單的代碼位。

public static Map<String, Set<LocalDate>> createDateMapping(LocalDate min, LocalDate max) throws ParseException { 
    DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd"); 
    DateTimeFormatter wrongFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy"); 

    final Map<String, Set<LocalDate>> inputMappings = new LinkedHashMap<>(); 

    for (LocalDate date = min; !date.isAfter(max); date = date.plusDays(1)) { 
     final String incorrectlyFormattedDate = date.format(wrongFormat); 
     final String key = targetFormat.format(targetFormat.parse(incorrectlyFormattedDate)); 
     if (!inputMappings.containsKey(key)) { 
      inputMappings.put(key, new TreeSet<>()); 
     } 
     inputMappings.get(key).add(date); 
    } 

    return inputMappings; 
} 

輕鬆修復無效日期取決於有效日期的範圍。
例如,如果max=2016-12-31那麼下表顯示了獨特的日期是可以解決的/含糊取決於min

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 

爲無效日期不明確的比賽在大約30年爲間隔發生,所以如果實際的日期落在一個數量30年的時間,那麼你很幸運

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

我不認爲你將能夠找出損壞的輸入原來的日期,但你應該能夠找到所有損壞的日期,可能會找到一種方法來重新消費該數據。這是因爲每個日期由未知的天數改變,並扭轉這一進程將需要你知道要麼的起始日期,它看起來像你沒有在這裏做的數量。

也就是說,實際上很容易縮小任何被破壞的日期。

您在一個月內獲得的最大價值應該是12.這意味着您的損壞數據的最新「年份」將是第12年。如果您的日期一直運行到現在,最大的一年被錯誤地解析爲天)將是2016年,這將被轉換爲約5.5年。因此,年齡低於18歲或19歲的任何日期都會被破壞,您至少應該能夠將其刪除。

唯一的邊緣情況在這裏,如果你有一個有多年將在十幾歲的時候有效方式土地的日期。如果是這樣的話,你必須親自去做。但這似乎不太可能。

-1

你有沒有嘗試設置的SimpleDateFormat寬鬆

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

逆轉日期1980年7月6日

解析日期1980年6月7日

+1

問題不在於,如何正確解析日期,或者如何避免錯誤的解析 - 這是關於從已經錯誤的解析日期到原始輸入的結論, –

2

我找到了一個方法o找到可能的輸入:

我可以使用日曆通過可能的日期進行迭代,解析日期在「wron」 G方式,並建立與這些信息的地圖。

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

通過使用這種方法,我可以的:

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] 

它不會完全解決問題,但至少是一個很好的起點 - 希望有一些日期有更明確的結果。