[Fix] Fixed PeriodHelper.shiftByMultipleOfPeriod()

There was an issue, where shifting by P2D would lead to wrong start date
This commit is contained in:
Robert von Burg 2022-04-12 16:22:50 +02:00
parent 89751f12d3
commit 4dfea7d589
1 changed files with 33 additions and 32 deletions

View File

@ -2,10 +2,8 @@ package li.strolch.utils.time;
import static java.time.Period.between;
import java.time.Duration;
import java.time.Month;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import li.strolch.utils.dbc.DBC;
@ -131,22 +129,22 @@ public class PeriodHelper {
}
/**
* This special function allows us end shift a start by a multiple of the given {@link PeriodDuration} so that is
* before the given end start. It does multiple tries end get as close as possible, due end the inexactness of 30
* This special function allows us to shift a date by a multiple of the given {@link PeriodDuration} so that is
* before the given end date. It does multiple tries end get as close as possible, due to the inexactness of 30
* days being one month, and 365 days being one year.
*
* @param date
* the start date to shift before the end
* @param dateWithTime
* the start date to shift before the end date
* @param end
* the start before which end stop shifting
* the date to which the given date may be shifted
* @param periodDuration
* the period shift in multiples by
* the period with which to shift the date with. Shifting is done in multiples of this given period
*
* @return the shifted start
* @return the shifted date
*/
public static ZonedDateTime shiftByMultipleOfPeriod(ZonedDateTime date, ZonedDateTime end,
public static ZonedDateTime shiftByMultipleOfPeriod(ZonedDateTime dateWithTime, ZonedDateTime end,
PeriodDuration periodDuration) {
DBC.PRE.assertTrue("date must be before end!", date.isBefore(end));
DBC.PRE.assertTrue("date must be before end!", dateWithTime.isBefore(end));
DBC.PRE.assertFalse("period duration may not be null!", periodDuration.isZero());
Duration duration = periodDuration.getDuration();
@ -156,9 +154,9 @@ public class PeriodHelper {
throw new UnsupportedOperationException(
"Shifting by Periods and Durations at the same time is not supported!");
Period between = between(date.toLocalDate(), end.toLocalDate());
Period between = between(dateWithTime.toLocalDate(), end.toLocalDate());
if (between.isZero() || daysIn(between) < 1)
return date;
return dateWithTime;
// see if we need end shift by years
if (period.getYears() > 0 && period.getMonths() == 0 && period.getDays() == 0 && duration.isZero()) {
@ -172,9 +170,9 @@ public class PeriodHelper {
shiftYears -= numberOfYears;
if (shiftYears < numberOfYears)
return date;
return dateWithTime;
return date.plusYears(shiftYears);
return dateWithTime.plusYears(shiftYears);
}
// see if we need end shift by months
@ -192,10 +190,10 @@ public class PeriodHelper {
shiftMonths -= numberOfMonths;
if (shiftMonths < numberOfMonths)
return date;
return dateWithTime;
// use special shift, so we keep end of month if applicable
return shiftMonths(date, shiftMonths);
return shiftMonths(dateWithTime, shiftMonths);
}
double daysInPeriod = daysIn(periodDuration);
@ -211,24 +209,27 @@ public class PeriodHelper {
long shiftWeeks = shifts * numberOfWeeks;
if (shiftWeeks < numberOfWeeks)
return date;
return dateWithTime;
ZonedDateTime result = date.plusWeeks(shiftWeeks);
ZonedDateTime result = dateWithTime.plusWeeks(shiftWeeks);
if (result.isBefore(end))
return result;
return date.plusWeeks(shiftWeeks - 1);
return dateWithTime.plusWeeks(shiftWeeks - 1);
}
// see if we are shifting simply by single days
// this includes 24h durations
if (daysInPeriod == 1.0)
return date.plus(between.minusDays(1));
return dateWithTime.plus(between.minusDays(1));
double daysInBetween = daysIn(between);
// e.g. period is P70D
if (daysInPeriod > daysInBetween)
return date;
return dateWithTime;
LocalTime localTime = dateWithTime.toLocalTime();
ZonedDateTime dateNoTime = dateWithTime.truncatedTo(ChronoUnit.DAYS);
// if shifting by more than one day e.g. P2D or P75D
if (daysInPeriod > 1.0) {
@ -236,14 +237,14 @@ public class PeriodHelper {
long shifts = (long) (daysInBetween / daysInPeriod);
long shiftDays = (long) (shifts * daysInPeriod);
if (shiftDays < 1)
return date;
return dateWithTime;
ZonedDateTime result = date.plusDays(shiftDays);
while (!result.isBefore(end)) {
shiftDays--;
result = date.plusDays(shiftDays);
ZonedDateTime result = dateNoTime.plusDays(shiftDays);
while (result.isAfter(end)) {
shiftDays =- (long) daysInPeriod;
result = dateNoTime.plusDays(shiftDays);
}
return result;
return result.toLocalDate().atTime(localTime).atZone(ZoneId.systemDefault());
}
if (!period.isZero())
@ -256,8 +257,8 @@ public class PeriodHelper {
long shifts = (long) (hoursInBetween / hoursInPeriod);
long shiftHours = (long) (shifts * hoursInPeriod);
if (shiftHours < 24)
return date;
return dateWithTime;
return date.plusDays((shiftHours / 24) - 1);
return dateWithTime.plusDays((shiftHours / 24) - 1);
}
}