Вот отправная точка. Я предполагаю, что вы захотите уточнить это дальше.
private static Pattern relativeTimePattern
= Pattern.compile("(\\w+)\\s*(?:([+-])\\s*(\\w+))?");
private static Map<String, Supplier<LocalDateTime>> bases
= Map.of("now", () -> LocalDateTime.now(),
"yesterday", () -> LocalDate.now().minusDays(1).atStartOfDay());
public static LocalDateTime parseRelativeTime(String timeString) {
Matcher m = relativeTimePattern.matcher(timeString);
if (m.matches()) {
String baseString = m.group(1);
LocalDateTime result = bases.get(baseString).get();
String signString = m.group(2);
if (signString != null) {
boolean subtract = signString.equals("-");
String diffString = m.group(3);
TemporalAmount diff;
try {
diff = Period.parse("P" + diffString);
} catch (DateTimeParseException dtpe) {
// try a Duration instead
diff = Duration.parse("PT" + diffString);
}
if (subtract) {
result = result.minus(diff);
} else {
result = result.plus(diff);
}
}
return result;
} else {
throw new IllegalArgumentException();
}
}
Давайте попробуем это:
System.out.println(parseRelativeTime("now - 5d"));
System.out.println(parseRelativeTime("yesterday"));
System.out.println(parseRelativeTime("now + 8d"));
Вывод, когда я только что запустил:
2020-03-30T09:49:18.300731
2020-04-03T00:00
2020-04-12T09:49:18.307784
Поскольку мой метод стоит, он принимает либо now
и yesterday
в нижнем регистре, за которыми необязательно следует знак (+
или -
) и либо точка лет-месяцев-недель-дней или продолжительность часов-минут-секунд. Каждый из последних должен использовать одну буквенную аббревиатуру для единиц времени (y, m, w, d, h, m, s; когда двусмысленная m приходит одна, она принимается за месяцы). Внутри периода или продолжительности запрещены пробелы.
Возможные дальнейшие разработки включают:
- Добавить больше слов:
today
, tomorrow
; рассмотрите возможность использования заглавных и смешанных букв. - Разрешить единицы измерения полностью и с пробелом, например
8 days
. Дальнейшее регулярное выражение преобразует это в 8d
перед последним анализом в Period
. - Запретить неоднозначное
2m
, заставляя пользователя указать, например, 2m0d
(2 месяца 0 дней) или 2m0s
(2 минуты 0 секунд). Или задайте прописные буквы M
для месяцев и строчные буквы m
для минут. - Сложная часть: предоставьте полезные сообщения об ошибках для строк, которые не могут быть проанализированы.