"Обратный" неверный разбор даты

102
17

мы запускаем REST-webservice, который потребляет разные данные, моя текущая проблема относится к дате, полученной как String, и анализируется с помощью java.text.SimpleDateFormat(java 8):


Мы получили много ( > 50k) "неправильных" форматированных строк, которые в любом случае анализировались 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" и, возможно, больше...)?


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

спросил(а) 2021-01-19T20:31:41+03:00 6 месяцев, 1 неделя назад
1
Решение
78

Я нашел способ найти возможные входы:


Я могу использовать 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]


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

ответил(а) 2021-01-19T20:31:41+03:00 6 месяцев, 1 неделя назад
64

Основываясь на Ответ Мартина Акермана:


Прежде всего, я немного упростил код.


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

ответил(а) 2021-01-19T20:31:41+03:00 6 месяцев, 1 неделя назад
45

Я не думаю, что вы сможете выяснить исходную дату поврежденного ввода, но вы сможете найти все поврежденные даты и, возможно, найти способ повторно использовать эти данные. Это связано с тем, что каждая дата была изменена на неизвестное количество дней, и для отмены этого процесса вам потребуется знать либо количество дней, либо дату начала, и похоже, что у вас этого нет.


Тем не менее, на самом деле будет довольно легко сузить любые даты, которые были повреждены.


Самое большое значение, которое вы получите в течение месяца, должно быть 12. Это означает, что последний "год" для ваших поврежденных данных будет годом 12. Если ваши даты будут работать вплоть до настоящего времени, самый большой год (который был неправильно разобранный как дни) будет 2016, который будет преобразован примерно в 5,5 лет. Таким образом, любые даты с годами ниже 18 или 19 повреждены, и вы должны иметь возможность, по крайней мере, удалить их.


Единственным краевым случаем здесь является то, что у вас есть даты, у которых есть годы, которые будут действительным образом приземляться в раннем подростковом возрасте. Если это произойдет, вам придется пройти через это вручную. Но это кажется маловероятным.

ответил(а) 2021-01-19T20:31:41+03:00 6 месяцев, 1 неделя назад
-5

Вы пробовали установить SimpleDateFormat Ленент на false


    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


ответил(а) 2021-01-19T20:31:41+03:00 6 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема