FakePeriod.java
package fr.avenirsesr.portfolio.api.infrastructure.adapter.seeder.fake;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.function.Function;
import lombok.Getter;
@Getter
public class FakePeriod<T extends Temporal> {
private static final FakerProvider faker = new FakerProvider();
private static final int academicYear = 2024;
private final Function<LocalDate, T> localDateConverter;
private final Function<Instant, T> instantConverter;
private T startDate;
private T endDate;
public static FakePeriod<Instant> createInstantPeriod() {
return new FakePeriod<>(
localDate -> {
int hour = faker.call().number().numberBetween(8, 20);
int minute = faker.call().number().numberBetween(0, 60);
return localDate.atTime(hour, minute).atZone(ZoneId.systemDefault()).toInstant();
},
instant -> instant);
}
private FakePeriod(
Function<LocalDate, T> localDateConverter, Function<Instant, T> instantConverter) {
this.localDateConverter = localDateConverter;
this.instantConverter = instantConverter;
}
public void initStartDateInAcademicPeriodBeforeMay() {
LocalDate startBoundary = LocalDate.of(academicYear, Month.SEPTEMBER, 1);
LocalDate endBoundary = LocalDate.of(academicYear + 1, Month.MAY, 31);
long startEpochDay = startBoundary.toEpochDay();
long endEpochDay = endBoundary.toEpochDay();
long randomDay =
startEpochDay
+ faker.call().number().numberBetween(0, (int) (endEpochDay - startEpochDay + 1));
LocalDate randomLocalDate = LocalDate.ofEpochDay(randomDay);
startDate = localDateConverter.apply(randomLocalDate);
}
public void initEndDateInAcademicPeriodAfterStartDate() {
if (startDate == null) {
initStartDateInAcademicPeriodBeforeMay();
}
Instant startInstant = convertStartDateToInstant();
Instant minimumEndDate = startInstant.plus(24, ChronoUnit.HOURS);
Instant baseEndDate = calculateBaseEndDate(minimumEndDate);
if (!(startDate instanceof LocalDate)) {
baseEndDate = adjustTimeForInstantType(startInstant, baseEndDate);
}
endDate = instantConverter.apply(baseEndDate);
}
private Instant convertStartDateToInstant() {
if (startDate instanceof LocalDate) {
return ((LocalDate) startDate).atStartOfDay(ZoneId.systemDefault()).toInstant();
} else {
return (Instant) startDate;
}
}
private Instant calculateBaseEndDate(Instant minimumEndDate) {
LocalDate julyFirstBoundary = LocalDate.of(academicYear + 1, Month.JULY, 1);
Instant julyFirstInstant = julyFirstBoundary.atStartOfDay(ZoneId.systemDefault()).toInstant();
if (minimumEndDate.isAfter(julyFirstInstant)) {
return julyFirstInstant.minus(1, ChronoUnit.DAYS);
}
return calculateRandomEndDate(minimumEndDate, julyFirstBoundary);
}
private Instant calculateRandomEndDate(Instant minimumEndDate, LocalDate julyFirstBoundary) {
long daysUntilJulyFirst =
ChronoUnit.DAYS.between(
minimumEndDate.atZone(ZoneId.systemDefault()).toLocalDate(), julyFirstBoundary);
int additionalDays =
faker.call().number().numberBetween(1, (int) Math.min(180, daysUntilJulyFirst));
return minimumEndDate.plus(additionalDays, ChronoUnit.DAYS);
}
private Instant adjustTimeForInstantType(Instant startInstant, Instant baseEndDate) {
int hour = faker.call().number().numberBetween(8, 20);
int minute = faker.call().number().numberBetween(0, 60);
LocalDate endLocalDate = baseEndDate.atZone(ZoneId.systemDefault()).toLocalDate();
Instant adjustedEndDate =
endLocalDate.atTime(hour, minute).atZone(ZoneId.systemDefault()).toInstant();
if (ChronoUnit.HOURS.between(startInstant, adjustedEndDate) < 24) {
return startInstant.plus(24, ChronoUnit.HOURS);
}
return adjustedEndDate;
}
public static FakePeriod<Instant> createMin24hoursInstantPeriodInAcademicPeriod() {
FakePeriod<Instant> fakePeriod = createInstantPeriod();
fakePeriod.initStartDateInAcademicPeriodBeforeMay();
fakePeriod.initEndDateInAcademicPeriodAfterStartDate();
return fakePeriod;
}
}