2016-07-19 3 views
4

мы запускаем REST-WebService, который потребляет различные данные, мой текущий номер принадлежит к дате, полученные в качестве строки и анализируется с помощью java.text.SimpleDateFormat (Java 8):«Reverse» неправильно разобраны дата

Мы получил много (> 50 тыс.) «неправильных» форматированных строк, которые в любом случае анализировались SimpleDateFormat.

В SimpleDateFormat настроен шаблон «yyyy-MM-dd». Мы получили Strings наоборот: «dd-MM-yyyy».

Например, строка «07-07-1950» была разобрана до даты «0012-10-31» (начиная с июля в 7-м году, добавлено в течение 1950 дней).

Мы исправили реализацию, поэтому эти строки теперь разбираются, как ожидалось. Но у нас есть все коррумпированные даты в системе. Последний вопрос:

Есть ли способ заключить с даты «0012-10-31» на возможные исходные входы (например, «07-07-1950», «07-06-1980» и, возможно, больше ...)?

С наилучшими пожеланиями

+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' теперь [наследие] (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. См. [* Tutorial * by 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));   
    }   
}   

Перевернутое Дата 07-06-1980

разобранную дата 1980-06-07

+1

Вопрос не в том, как правильно разобрать дату, или как избежать неправильного разбора - речь шла о выводе из уже неверной разобранной даты на исходный вход (-ы) –

2

Я нашел способ t 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] 

Это не решит проблему полностью, но по крайней мере, хорошая отправная точка - надеюсь, есть некоторые даты с более явными результатами.

Смежные вопросы