"反向"错误的解析日期

时间:2016-07-19 09:37:27

标签: java date simpledateformat

我们运行一个REST-webservice,它使用不同的数据,我当前的问题属于一个日期,作为String接收并由java.text.SimpleDateFormat(java 8)解析:

我们收到了很多(> 50k)“错误”的错误信息。格式化的字符串,无论如何都是由SimpleDateFormat解析的。

SimpleDateFormat配置了模式" yyyy-MM-dd"。 我们以相反的方式收到了Strings" dd-MM-yyyy"。

例如String" 07-07-1950"被解析为日期" 0012-10-31" (从7年7月开始,增加1950天)。

我们修复了实现,因此现在可以按预期解析这些字符串。但是我们在系统中有所有损坏的日期。最后一个问题是:

有没有办法从日期结束" 0012-10-31"可能的原始输入(例如" 07-07-1950"," 07-06-1980"以及更多......)?

祝你好运

4 个答案:

答案 0 :(得分:2)

我找到了找到可能输入的方法:

我可以使用Calendar来迭代可能的日期,以" 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]

它不能完全解决问题,但至少是一个很好的起点 - 希望有些日期有更明确的结果。

答案 1 :(得分: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

答案 2 :(得分:0)

我不认为您能够找出损坏输入的原始日期,但您应该能够找到所有损坏的日期,并且可能找到重新使用该数据的方法。这是因为每个日期都被未知的天数更改,并且逆转该过程将要求您知道 <天> 开始日期的天数,并且它看起来就像你在这里没有那样。

尽管如此,实际上很容易缩小任何已损坏的日期。

一个月的最大值应为12.这意味着最新的&#34;年&#34;对于您的损坏数据将是12年。如果您的日期一直持续到现在,最大的一年(被错误地解析为天)将是2016年,将转换为大约5。5年。因此,年龄低于18或19岁的任何日期都已损坏,您应该至少可以将其删除。

这里唯一的优势就是如果你的日期有多年才会有效地落在青少年时期。如果是这样的话,你必须手工完成。但这似乎不太可能。

答案 3 :(得分:-1)

您是否尝试将 SimpleDateFormat 宽容设置为 false

this.ViewModel.GetDateTime();// WCF async call

while (this.ViewModel.wcfCallInProgress)
{
    Thread.Sleep(10);
}

this.ViewModel.UpdateValue(value); // another WCF async call, need value from GetDateTime() method's return result.
this.ViewModel.UpdateAnotherValue(value, user); // third WCF async call
  

撤消日期07-06-1980

     

解析日期1980-06-07