add date parser for relative time notation

This commit is contained in:
2024-05-05 08:40:30 +02:00
parent 6d6b6ba00c
commit a99a884423
2 changed files with 212 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
package org.lucares.pdb.plot.api;
import java.time.DayOfWeek;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.lucares.pdb.api.DateTimeRange;
public class DateTimeRangeParser {
public static DateTimeRange parse(final OffsetDateTime offsetTime, final String datePeriod) {
final String[] startEnd = datePeriod.split(Pattern.quote("/"));
final String start = startEnd[0];
final String end = startEnd[1];
final OffsetDateTime startTime = parseInternal(offsetTime, start);
final OffsetDateTime endTime = parseInternal(offsetTime, end);
return new DateTimeRange(startTime, endTime);
}
private static OffsetDateTime parseInternal(final OffsetDateTime offsetTime, final String timeDefinition) {
final Pattern regex = Pattern.compile("(?<beginEnd>[BE]?)(?<amount>\\-?[0-9]*)(?<unit>[mHDWMY])");
final Matcher matcher = regex.matcher(timeDefinition);
if (matcher.matches()) {
final String beginEnd = matcher.group("beginEnd");
final boolean begin = "B".equals(beginEnd);
final String amountString = matcher.group("amount");
final int amount = amountString.equals("") ? 0 : Integer.parseInt(amountString);
final String unitString = matcher.group("unit");
switch (unitString) {
case "m": {
final ChronoUnit unit = ChronoUnit.MINUTES;
if (begin) {
return offsetTime.plus(amount, unit).truncatedTo(unit);
} else {
return offsetTime.plus(amount + 1, unit).truncatedTo(unit).minusSeconds(1);
}
}
case "H": {
final ChronoUnit unit = ChronoUnit.HOURS;
if (begin) {
return offsetTime.plus(amount, unit).truncatedTo(unit);
} else {
return offsetTime.plus(amount + 1, unit).truncatedTo(unit).minusSeconds(1);
}
}
case "D": {
final ChronoUnit unit = ChronoUnit.DAYS;
if (begin) {
return offsetTime.plus(amount, unit).truncatedTo(unit);
} else {
return offsetTime.plus(amount + 1, unit).truncatedTo(unit).minusSeconds(1);
}
}
case "W": {
final DayOfWeek firstDayOfWeek = DayOfWeek.MONDAY;
final DayOfWeek lastDayOfWeek = DayOfWeek
.of(((firstDayOfWeek.getValue() - 1 + 6) % DayOfWeek.values().length) + 1); // weird
// computation,
// because DayOfWeek
// goes from 1 to 7
final ChronoUnit unit = ChronoUnit.WEEKS;
if (begin) {
return offsetTime.plus(amount, unit).with(TemporalAdjusters.previousOrSame(firstDayOfWeek))
.truncatedTo(ChronoUnit.DAYS);
} else {
return offsetTime.plus(amount, unit).with(TemporalAdjusters.nextOrSame(lastDayOfWeek))
.plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS).minusSeconds(1);
}
}
case "M": {
final ChronoUnit unit = ChronoUnit.MONTHS;
if (begin) {
return offsetTime.plus(amount, unit).truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
} else {
return offsetTime.plus(amount, unit).truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1)
.plus(1, ChronoUnit.MONTHS).minusSeconds(1);
}
}
case "Y": {
final ChronoUnit unit = ChronoUnit.YEARS;
if (begin) {
return offsetTime.plus(amount, unit).truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
} else {
return offsetTime.plus(amount, unit).truncatedTo(ChronoUnit.DAYS).withDayOfYear(1)
.plus(1, ChronoUnit.YEARS).minusSeconds(1);
}
}
default:
throw new IllegalArgumentException("Unexpected value: " + unitString);
}
}
throw new IllegalArgumentException("invalid input: " + timeDefinition);
}
}

View File

@@ -0,0 +1,107 @@
package org.lucares.pdb.plot.api;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lucares.pdb.api.DateTimeRange;
public class DateTimeRangeParserTest {
@Test
public void test() {
}
public static Stream<Arguments> providerDatePeriods() {
return Stream.of(//
// last 15 minutes
Arguments.of("2000-01-02 12:59:59", "B-14m/Em", "2000-01-02 12:45:00", "2000-01-02 12:59:59"),
// this hour
Arguments.of("2000-01-02 12:00:00", "BH/EH", "2000-01-02 12:00:00", "2000-01-02 12:59:59"),
Arguments.of("2000-01-02 12:59:59", "BH/EH", "2000-01-02 12:00:00", "2000-01-02 12:59:59"),
// previous hour
Arguments.of("2000-01-02 12:00:00", "B-1H/E-1H", "2000-01-02 11:00:00", "2000-01-02 11:59:59"),
Arguments.of("2000-01-02 12:59:58", "B-1H/E-1H", "2000-01-02 11:00:00", "2000-01-02 11:59:59"),
Arguments.of("2000-01-02 12:59:59", "B-1H/E-1H", "2000-01-02 11:00:00", "2000-01-02 11:59:59"),
// today
Arguments.of("2000-01-02 12:00:00", "BD/ED", "2000-01-02 00:00:00", "2000-01-02 23:59:59"),
Arguments.of("2000-01-02 00:00:00", "BD/ED", "2000-01-02 00:00:00", "2000-01-02 23:59:59"),
Arguments.of("2000-01-02 23:59:59", "BD/ED", "2000-01-02 00:00:00", "2000-01-02 23:59:59"),
Arguments.of("2000-01-02 12:00:00", "B0D/E0D", "2000-01-02 00:00:00", "2000-01-02 23:59:59"),
// tomorrow
Arguments.of("2000-01-02 12:00:00", "B1D/E1D", "2000-01-03 00:00:00", "2000-01-03 23:59:59"),
// yesterday
Arguments.of("2000-01-02 12:00:00", "B-1D/E-1D", "2000-01-01 00:00:00", "2000-01-01 23:59:59"),
// this week
Arguments.of("2024-04-22 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-23 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-24 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-25 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-26 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-27 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
Arguments.of("2024-04-28 12:00:00", "BW/EW", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
// previous week
Arguments.of("2024-04-29 12:00:00", "B-1W/E-1W", "2024-04-22 00:00:00", "2024-04-28 23:59:59"),
// last 4 week (including this one
Arguments.of("2024-04-28 12:00:00", "B-3W/EW", "2024-04-01 00:00:00", "2024-04-28 23:59:59"),
// this month
Arguments.of("2024-04-29 12:00:00", "BM/EM", "2024-04-01 00:00:00", "2024-04-30 23:59:59"),
// previous month (in a leap year)
Arguments.of("2023-03-29 12:00:00", "B-1M/E-1M", "2023-02-01 00:00:00", "2023-02-28 23:59:59"),
// previous month (in a leap year)
Arguments.of("2024-03-29 12:00:00", "B-1M/E-1M", "2024-02-01 00:00:00", "2024-02-29 23:59:59"),
// last 3 months
Arguments.of("2024-03-29 12:00:00", "B-2M/EM", "2024-01-01 00:00:00", "2024-03-31 23:59:59"),
// previous 3 months (in a leap year)
Arguments.of("2024-03-29 12:00:00", "B-3M/E-1M", "2023-12-01 00:00:00", "2024-02-29 23:59:59"),
// this year
Arguments.of("2024-03-29 12:00:00", "BY/EY", "2024-01-01 00:00:00", "2024-12-31 23:59:59"),
// previous year
Arguments.of("2024-03-29 12:00:00", "B-1Y/E-1Y", "2023-01-01 00:00:00", "2023-12-31 23:59:59")
//
// Arguments.of("2024-03-29 12:00:00", "B-1H-15m/Bm", "2024-03-29 10:45:00",
// "2024-03-29 12:00:00")
);
}
@ParameterizedTest
@MethodSource("providerDatePeriods")
public void testDatePeriods(final String now, final String datePeriod, final String expectedStart,
final String expectedEnd) throws Exception {
final OffsetDateTime offsetTime = LocalDateTime.parse(now, PlotSettings.DATE_FORMAT).atOffset(ZoneOffset.UTC);
final DateTimeRange actual = DateTimeRangeParser.parse(offsetTime, datePeriod);
final String actualStart = PlotSettings.DATE_FORMAT.format(actual.getStart());
final String actualEnd = PlotSettings.DATE_FORMAT.format(actual.getEnd());
System.out.println("at " + now + " " + datePeriod + " -> " + actualStart + " - " + actualEnd);
Assertions.assertEquals(expectedStart, actualStart, "start");
Assertions.assertEquals(expectedEnd, actualEnd, "end");
}
}