[New] added PeriodHelper.shiftDate() and fixed .shiftByMultipleOfPeriod() with tests

This commit is contained in:
Robert von Burg 2021-04-29 21:19:42 +02:00
parent 0967b413ac
commit cf56240bc6
2 changed files with 687 additions and 177 deletions

View File

@ -2,20 +2,16 @@ 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.util.concurrent.TimeUnit;
import li.strolch.utils.dbc.DBC;
public class PeriodHelper {
public static double monthsIn(PeriodDuration periodDuration) {
long hours = periodDuration.getDuration().toHours();
Period period = periodDuration.getPeriod();
long months = period.toTotalMonths();
return (months + (period.getDays() / 30.0) + (hours / 24.0 / 30.0));
}
public static double daysIn(PeriodDuration periodDuration) {
return (daysIn(periodDuration.getPeriod()) + (periodDuration.getDuration().toHours() / 24.0));
}
@ -24,14 +20,11 @@ public class PeriodHelper {
return (period.getYears() * 365.0) + (period.getMonths() * 30.0) + period.getDays();
}
private static double monthsIn(Period period) {
return (period.getYears() * 12.0) + (period.getMonths());
}
/**
* Special method to add the given number of months to the given date and making sure that the day always stays the
* same if possible. I.e. If the given date has the 3. day, then this will also be so on the returned date. But for
* the day 29, 30 or 31 the date might change to 28 or 30, depending on the month on which the result lies.
* <p>Special method to add the given number of months to the given date and making sure that the day always stays
* the same if possible. I.e. If the given date has the 3. day, then this will also be so on the returned date.</p>
* <p>But for the day 28, 29, 30 or 31, if these are the last days of the given month, then the returned month is
* also shifted to the last day</p>
*
* @param date
* the date to shift
@ -40,59 +33,223 @@ public class PeriodHelper {
*
* @return the shifted date
*/
public static ZonedDateTime shiftMonths(ZonedDateTime date, int nrOfMonths) {
public static ZonedDateTime shiftMonths(ZonedDateTime date, long nrOfMonths) {
int selectedDayOfMonth = date.getDayOfMonth();
ZonedDateTime next = date.plusMonths(nrOfMonths);
if (date.toLocalDate().lengthOfMonth() == selectedDayOfMonth)
return next.withDayOfMonth(next.toLocalDate().lengthOfMonth());
return next.withDayOfMonth(Math.min(selectedDayOfMonth, next.toLocalDate().lengthOfMonth()));
}
/**
* This special function allows us to shift a date by a multiple of the given {@link PeriodDuration} so that is
* before the given to date. It does multiple tries to get as close as possible, due to the inexactness of 30 days
* being one month, and 365 days being one year.
* Shifts the given date by the given period duration. This method doesn't accept a mixture of period and duration,
* one or the other must be zero. Durations may only contain hours and minutes. Periods must only have one part set,
* i.e. either year, month or day. Months are handles special in that the shifting is delegated to {@link
* #shiftMonths(ZonedDateTime, long)}. Furthermore, this method also specially handles weeks, i.e. if shifting is by
* multiple of 7, then this is handled as shifting by weeks
*
* @param date
* the date to shift
* @param to
* the date before which to stop shifting
* @param periodDuration
* the period shift in multiples by
* the period duration to shift the date by
*
* @return the shifted date
*/
public static ZonedDateTime shiftByMultipleOfPeriod(ZonedDateTime date, ZonedDateTime to,
PeriodDuration periodDuration) {
DBC.PRE.assertTrue("date must be before to!", date.isBefore(to));
DBC.PRE.assertFalse("period duration may not be null!", periodDuration.isZero());
public static ZonedDateTime shiftDate(ZonedDateTime date, PeriodDuration periodDuration) {
DBC.PRE.assertNotNull("date may not be null!", date);
DBC.PRE.assertNotNull("periodDuration may not be null!", periodDuration);
// see if we need to shift by months
long monthsInPeriod = (long) monthsIn(periodDuration);
if (monthsInPeriod > 0) {
Period between = between(date.toLocalDate(), to.toLocalDate());
double monthsInBetween = monthsIn(between);
long shifts = (long) (monthsInBetween / monthsInPeriod);
if (shifts > 0) {
date = date.plusMonths(shifts);
}
}
Period between = between(date.toLocalDate(), to.toLocalDate());
double daysInBetween = daysIn(between);
double daysInPeriod = daysIn(periodDuration);
long shifts = (long) (daysInBetween / daysInPeriod);
if (shifts < 0)
if (periodDuration.isZero())
return date;
ZonedDateTime shiftedDate = date.plusDays((long) (shifts * daysInPeriod));
Duration duration = periodDuration.getDuration();
Period period = periodDuration.getPeriod();
// see if we are close enough now
between = between(shiftedDate.toLocalDate(), to.toLocalDate());
daysInBetween = daysIn(between);
shifts = (long) (daysInBetween / daysInPeriod);
if (shifts < 2)
return shiftedDate;
if (!period.isZero() && !duration.isZero())
throw new UnsupportedOperationException(
"Shifting by Periods and Durations at the same time is not supported!");
return shiftByMultipleOfPeriod(shiftedDate, to, periodDuration);
if (!period.isZero()) {
// shift by years
if (period.getYears() > 0) {
if (period.getMonths() != 0 && period.getDays() != 0)
throw new UnsupportedOperationException(
"Shifting by multi values not supported: " + periodDuration);
int numberOfYears = period.getYears();
ZonedDateTime shifted = date.plusYears(numberOfYears);
// if the given month was on the last day of february, then keep the last day in the new year as well
if (date.getMonth() == Month.FEBRUARY && date.getDayOfMonth() == date.toLocalDate().lengthOfMonth())
shifted = shifted.withDayOfMonth(shifted.toLocalDate().lengthOfMonth());
return shifted;
}
// shift by months
if (period.getMonths() > 0) {
if (period.getDays() != 0)
throw new UnsupportedOperationException(
"Shifting by multi values not supported: " + periodDuration);
int numberOfMonths = period.getMonths();
// use special shift, so we keep end of month if applicable
return shiftMonths(date, numberOfMonths);
}
// shift by weeks
if (period.getDays() % 7 == 0) {
int numberOfWeeks = period.getDays() / 7;
return date.plusWeeks(numberOfWeeks);
}
// shift by days
int numberOfDays = period.getDays();
return date.plusDays(numberOfDays);
}
// shift by hours
long hours = duration.toHours();
if (hours > 0) {
date = date.plusHours(hours);
duration = duration.minusHours(hours);
}
// shift by minutes
long minutes = duration.toMinutes();
if (minutes > 0) {
date = date.plusMinutes(minutes);
duration = duration.minusMinutes(minutes);
}
if (duration.toSecondsPart() > 0 || duration.toMillisPart() > 0)
throw new UnsupportedOperationException("Only supporting hours and minutes: " + periodDuration);
return date;
}
/**
* 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
* days being one month, and 365 days being one year.
*
* @param date
* the start date to shift before the end
* @param end
* the start before which end stop shifting
* @param periodDuration
* the period shift in multiples by
*
* @return the shifted start
*/
public static ZonedDateTime shiftByMultipleOfPeriod(ZonedDateTime date, ZonedDateTime end,
PeriodDuration periodDuration) {
DBC.PRE.assertTrue("date must be before end!", date.isBefore(end));
DBC.PRE.assertFalse("period duration may not be null!", periodDuration.isZero());
Duration duration = periodDuration.getDuration();
Period period = periodDuration.getPeriod();
if (!period.isZero() && !duration.isZero())
throw new UnsupportedOperationException(
"Shifting by Periods and Durations at the same time is not supported!");
Period between = between(date.toLocalDate(), end.toLocalDate());
if (between.isZero() || daysIn(between) < 1)
return date;
// see if we need end shift by years
if (period.getYears() > 0 && period.getMonths() == 0 && period.getDays() == 0 && duration.isZero()) {
int numberOfYears = period.getYears();
// calculate the number of years to shift
long yearsBetween = between.getYears();
long shifts = yearsBetween / numberOfYears;
long shiftYears = shifts * numberOfYears;
if (shiftYears == yearsBetween)
shiftYears -= numberOfYears;
if (shiftYears < numberOfYears)
return date;
return date.plusYears(shiftYears);
}
// see if we need end shift by months
if (period.getMonths() > 0 && period.getYears() == 0 && period.getDays() == 0 && duration.isZero()) {
int numberOfMonths = period.getMonths();
// calculate the number of months to shift
long monthsBetween = between.toTotalMonths();
// we will accept 30 days as a full month, and increase as well
if (between.getDays() >= 30)
monthsBetween += 1;
long shifts = monthsBetween / numberOfMonths;
long shiftMonths = shifts * numberOfMonths;
if (shiftMonths == monthsBetween)
shiftMonths -= numberOfMonths;
if (shiftMonths < numberOfMonths)
return date;
// use special shift, so we keep end of month if applicable
return shiftMonths(date, shiftMonths);
}
double daysInPeriod = daysIn(periodDuration);
// see if we need end shift by weeks
if (period.getDays() % 7 == 0 && daysInPeriod % 7 == 0) {
int numberOfWeeks = period.getDays() / 7;
// calculate the number of weeks to shift
long daysInBetween = (long) daysIn(between);
long weeksBetween = daysInBetween / 7;
long shifts = weeksBetween / numberOfWeeks;
long shiftWeeks = shifts * numberOfWeeks;
if (shiftWeeks < numberOfWeeks)
return date;
return date.plusWeeks(shiftWeeks);
}
// see if we are shifting simply by single days
// this includes 24h durations
if (daysInPeriod == 1.0)
return date.plus(between.minusDays(1));
double daysInBetween = daysIn(between);
// e.g. period is P70D
if (daysInPeriod > daysInBetween)
return date;
// if shifting by more than one day e.g. P2D or P75D
if (daysInPeriod > 1.0) {
long shifts = (long) (daysInBetween / daysInPeriod);
long shiftDays = (long) (shifts * daysInPeriod);
if (shiftDays < 1)
return date;
return date.plusDays(shiftDays);
}
if (!period.isZero())
throw new IllegalStateException(
"Expected period to be zero at this point and only duration to be set: " + periodDuration);
// shifting by e.g. PT8H
long hoursInPeriod = duration.toHours();
long hoursInBetween = TimeUnit.DAYS.toHours((long) daysInBetween);
long shifts = hoursInBetween / hoursInPeriod;
long shiftHours = shifts * hoursInPeriod;
if (shiftHours < 24)
return date;
return date.plusDays(shiftHours / 24);
}
}

View File

@ -1,14 +1,12 @@
package li.strolch.utils.time;
import static java.time.ZoneId.systemDefault;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
import static li.strolch.utils.time.PeriodHelper.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.*;
import org.junit.Test;
@ -70,23 +68,159 @@ public class PeriodHelperTest {
}
@Test
public void shouldCalcMonths1() {
assertEquals(1, monthsIn(PeriodDuration.parse("P1M")), 0.0);
public void shouldShiftDateYears1() {
ZonedDateTime date = LocalDate.of(2020, 1, 31).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2021, 1, 31).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P1Y"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcMonths2() {
assertEquals(1, monthsIn(PeriodDuration.parse("P30D")), 0.0);
public void shouldShiftDateYears2() {
ZonedDateTime date = LocalDate.of(2020, 1, 31).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2022, 1, 31).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P2Y"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcMonths3() {
assertEquals(2, monthsIn(PeriodDuration.parse("P60D")), 0.0);
public void shouldShiftDateYears3() {
ZonedDateTime date = LocalDate.of(2020, 2, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2022, 2, 28).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P2Y"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcMonths4() {
assertEquals(12, monthsIn(PeriodDuration.parse("P1Y")), 0.0);
public void shouldShiftDateYears4() {
ZonedDateTime date = LocalDate.of(2018, 2, 28).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2020, 2, 29).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P2Y"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateMonth1() {
ZonedDateTime date = LocalDate.of(2018, 2, 28).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 4, 30).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P2M"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateMonth2() {
ZonedDateTime date = LocalDate.of(2018, 2, 28).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 31).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P3M"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateMonth3() {
ZonedDateTime date = LocalDate.of(2018, 5, 3).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 8, 3).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P3M"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateDay1() {
ZonedDateTime date = LocalDate.of(2018, 5, 3).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 6).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P3D"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateDay2() {
ZonedDateTime date = LocalDate.of(2018, 5, 3).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 13).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P10D"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateDay3() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 6, 8).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P10D"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateWeek1() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 6, 5).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P7D"));
assertEquals(shifted.toString(), expected, shifted);
assertEquals(shifted.toString(), expected.getDayOfWeek(), date.getDayOfWeek());
}
@Test
public void shouldShiftDateWeek2() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 6, 5).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P1W"));
assertEquals(shifted.toString(), expected, shifted);
assertEquals(shifted.toString(), expected.getDayOfWeek(), date.getDayOfWeek());
}
@Test
public void shouldShiftDateWeek3() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 6, 12).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P2W"));
assertEquals(shifted.toString(), expected, shifted);
assertEquals(shifted.toString(), expected.getDayOfWeek(), date.getDayOfWeek());
}
@Test
public void shouldShiftDateWeek4() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atStartOfDay(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 6, 12).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("P14D"));
assertEquals(shifted.toString(), expected, shifted);
assertEquals(shifted.toString(), expected.getDayOfWeek(), date.getDayOfWeek());
}
@Test
public void shouldShiftDateHour1() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(10, 0)).atZone(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(18, 0)).atZone(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("PT8H"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateHour2() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(22, 0)).atZone(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 30).atTime(LocalTime.of(6, 0)).atZone(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("PT8H"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateHour3() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(22, 0)).atZone(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 30).atTime(LocalTime.of(22, 0)).atZone(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("PT24H"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateMinute1() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(22, 0)).atZone(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(22, 30)).atZone(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("PT30M"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldShiftDateMinute2() {
ZonedDateTime date = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(22, 45)).atZone(systemDefault());
ZonedDateTime expected = LocalDate.of(2018, 5, 29).atTime(LocalTime.of(23, 0)).atZone(systemDefault());
ZonedDateTime shifted = shiftDate(date, PeriodDuration.parse("PT15M"));
assertEquals(shifted.toString(), expected, shifted);
}
@Test
@ -110,118 +244,46 @@ public class PeriodHelperTest {
}
@Test
public void shouldPlusMonths28() {
int expectedDay = 28;
ZonedDateTime date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
assertEquals(expectedDay, date.getDayOfMonth());
// increase by single months
for (int i = 0; i < 5; i++) {
date = shiftMonths(date, 1);
assertEquals(expectedDay, date.getDayOfMonth());
}
// also when increase by multiple months
for (int i = 0; i < 5; i++) {
date = shiftMonths(date, 4);
assertEquals(expectedDay, date.getDayOfMonth());
}
}
@Test
public void shouldPlusMonths29() {
int expectedDay = 29;
public void shouldPlusMonthsLast() {
ZonedDateTime date;
// increase by single months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY)
assertEquals(28, date.getDayOfMonth());
else
assertEquals(expectedDay, date.getDayOfMonth());
}
date = LocalDate.of(2020, 1, 31).atStartOfDay(systemDefault());
// february
date = shiftMonths(date, 1);
assertEquals(date.toString(), Month.FEBRUARY, date.getMonth());
assertEquals(date.toString(), Month.FEBRUARY.length(true), date.getDayOfMonth());
// march
date = shiftMonths(date, 1);
assertEquals(date.toString(), Month.MARCH, date.getMonth());
assertEquals(date.toString(), Month.MARCH.length(true), date.getDayOfMonth());
// april
date = shiftMonths(date, 1);
assertEquals(date.toString(), Month.APRIL, date.getMonth());
assertEquals(date.toString(), Month.APRIL.length(true), date.getDayOfMonth());
// may
date = shiftMonths(date, 1);
assertEquals(date.toString(), Month.MAY, date.getMonth());
assertEquals(date.toString(), Month.MAY.length(true), date.getDayOfMonth());
// also when increase by multiple months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY)
assertEquals(28, date.getDayOfMonth());
else
assertEquals(expectedDay, date.getDayOfMonth());
}
}
date = LocalDate.of(2020, 1, 31).atStartOfDay(systemDefault());
@Test
public void shouldPlusMonths30() {
int expectedDay = 30;
// march
date = shiftMonths(date, 2);
assertEquals(date.toString(), Month.MARCH, date.getMonth());
assertEquals(date.toString(), Month.MARCH.length(true), date.getDayOfMonth());
ZonedDateTime date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
assertEquals(expectedDay, date.getDayOfMonth());
// june
date = shiftMonths(date, 3);
assertEquals(date.toString(), Month.JUNE, date.getMonth());
assertEquals(date.toString(), Month.JUNE.length(true), date.getDayOfMonth());
// increase by single months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY)
assertEquals(28, date.getDayOfMonth());
else
assertEquals(expectedDay, date.getDayOfMonth());
}
// also when increase by multiple months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY)
assertEquals(28, date.getDayOfMonth());
else
assertEquals(expectedDay, date.getDayOfMonth());
}
}
@Test
public void shouldPlusMonths31() {
int expectedDay = 31;
ZonedDateTime date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
assertEquals(date.getMonth().name(), expectedDay, date.getDayOfMonth());
// increase by single months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY) {
assertEquals(date.getMonth().name(), 28, date.getDayOfMonth());
} else if (date.getMonth() == Month.APRIL //
|| date.getMonth() == Month.JUNE //
|| date.getMonth() == Month.SEPTEMBER //
|| date.getMonth() == Month.NOVEMBER) {
assertEquals(date.getMonth().name(), 30, date.getDayOfMonth());
} else {
assertEquals(date.getMonth().name(), expectedDay, date.getDayOfMonth());
}
}
// also when increase by multiple months
for (int i = 0; i < 5; i++) {
date = LocalDate.of(2001, Month.JANUARY, expectedDay).atStartOfDay(systemDefault());
date = shiftMonths(date, i);
if (date.getMonth() == Month.FEBRUARY) {
assertEquals(date.getMonth().name(), 28, date.getDayOfMonth());
} else if (date.getMonth() == Month.APRIL //
|| date.getMonth() == Month.JUNE //
|| date.getMonth() == Month.SEPTEMBER //
|| date.getMonth() == Month.NOVEMBER) {
assertEquals(date.getMonth().name(), 30, date.getDayOfMonth());
} else {
assertEquals(date.getMonth().name(), expectedDay, date.getDayOfMonth());
}
}
}
@Test
@ -231,23 +293,19 @@ public class PeriodHelperTest {
PeriodDuration periodDuration = PeriodDuration.parse("P1M");
ZonedDateTime shiftedDate = shiftByMultipleOfPeriod(past, now, periodDuration);
assertTrue(shiftedDate.isAfter(now.minusDays(10)));
assertTrue(shiftedDate.isBefore(now));
assertTrue(shiftedDate.toString(), shiftedDate.isAfter(now.minusMonths(2)));
}
@Test
public void shouldCalcShiftDays2() {
ZonedDateTime past = ZonedDateTime
.parse("2007-12-03T10:15:30+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(systemDefault()));
ZonedDateTime past = LocalDate.of(2007, 12, 3).atTime(LocalTime.now()).atZone(systemDefault());
ZonedDateTime now = ZonedDateTime.now();
PeriodDuration periodDuration = PeriodDuration.parse("P1M");
ZonedDateTime shiftedDate = shiftByMultipleOfPeriod(past, now, periodDuration);
// since P1M ist = 30 days, we have a rather inexact match, but it must certainly be
// after now() - P1M + 5
// before now()
assertTrue(shiftedDate.isAfter(now.minusDays(35)));
assertTrue(shiftedDate.isBefore(now));
assertEquals(shiftedDate.toString(), 3, shiftedDate.getDayOfMonth());
assertTrue(shiftedDate.toString(), shiftedDate.isBefore(now));
assertTrue(shiftedDate.toString(), shiftedDate.isAfter(now.minusMonths(2)));
}
@Test
@ -257,19 +315,314 @@ public class PeriodHelperTest {
PeriodDuration periodDuration = PeriodDuration.parse("P7D");
ZonedDateTime shiftedDate = shiftByMultipleOfPeriod(past, now, periodDuration);
assertTrue(shiftedDate.isAfter(now.minusDays(9)));
assertTrue(shiftedDate.toString(), shiftedDate.isAfter(now.minusDays(9)));
assertTrue(shiftedDate.isBefore(now));
assertEquals(past.getDayOfWeek(), shiftedDate.getDayOfWeek());
}
@Test
public void shouldCalcShiftDays4() {
ZonedDateTime past = ZonedDateTime
.parse("2007-12-03T10:15:30+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(systemDefault()));
.parse("2007-12-03T10:15:30+01:00", ISO_OFFSET_DATE_TIME.withZone(systemDefault()));
ZonedDateTime now = ZonedDateTime.now();
PeriodDuration periodDuration = PeriodDuration.parse("P7D");
ZonedDateTime shiftedDate = shiftByMultipleOfPeriod(past, now, periodDuration);
// since we are many years and months before now, and a year is 356 days and a month is 28 days, we inexact, but at least we must be a more than P7D before now
assertTrue(shiftedDate.isBefore(now));
assertTrue(shiftedDate.toString(), shiftedDate.isBefore(now));
}
@Test
public void shouldCalcShift7Days1Fixed() {
ZonedDateTime date1 = LocalDate.of(2017, 10, 30).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2020, 1, 2).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P7D"));
assertEquals(date1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2019, 12, 30).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShift7Days2Fixed() {
ZonedDateTime date1 = LocalDate.of(2017, 10, 30).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2021, 4, 28).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P7D"));
assertEquals(date1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShift7Days3Fixed() {
ZonedDateTime date1 = LocalDate.of(2021, 4, 21).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2021, 4, 28).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P7D"));
assertEquals(date1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 28).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShift1Days1Fixed() {
ZonedDateTime date1 = LocalDate.of(2017, 10, 30).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2020, 1, 2).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P1D"));
ZonedDateTime expected = LocalDate.of(2020, 1, 1).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShift1Days2Fixed() {
ZonedDateTime date1 = LocalDate.of(2017, 10, 30).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2021, 4, 28).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P1D"));
ZonedDateTime expected = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShift1Days3Fixed() {
ZonedDateTime date1 = LocalDate.of(2021, 4, 21).atStartOfDay(systemDefault());
ZonedDateTime date2 = LocalDate.of(2021, 4, 28).atStartOfDay(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(date1, date2, PeriodDuration.parse("P1D"));
ZonedDateTime expected = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftYears1Fixed() {
ZonedDateTime d1 = LocalDate.of(2007, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1Y"));
ZonedDateTime expected = LocalDate.of(2019, 6, 3).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftYears2Fixed() {
ZonedDateTime d1 = LocalDate.of(2007, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P2Y"));
ZonedDateTime expected = LocalDate.of(2019, 6, 3).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths1Fixed() {
ZonedDateTime d1 = LocalDate.of(2007, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 5, 3).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths2Fixed() {
ZonedDateTime d1 = LocalDate.of(2007, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 7, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 6, 3).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths3Fixed() {
ZonedDateTime d1 = LocalDate.of(2007, 6, 3).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 6, 2).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 5, 3).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths4Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 31).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 7, 30).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 6, 30).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths5Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 2, 28).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 7, 30).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 6, 30).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths6Fixed() {
ZonedDateTime d1 = LocalDate.of(2020, 2, 29).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 7, 30).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1M"));
ZonedDateTime expected = LocalDate.of(2020, 6, 30).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftMonths7Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 31).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2020, 7, 30).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P3M"));
ZonedDateTime expected = LocalDate.of(2020, 4, 30).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftWeek1Fixed() {
ZonedDateTime d1 = LocalDate.of(2021, 4, 6).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 28).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1W"));
assertEquals(d1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftWeek2Fixed() {
ZonedDateTime d1 = LocalDate.of(2021, 4, 6).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1W"));
assertEquals(d1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftWeek3Fixed() {
ZonedDateTime d1 = LocalDate.of(2021, 4, 24).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P7D"));
assertEquals(d1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 24).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftWeek4Fixed() {
ZonedDateTime d1 = LocalDate.of(2019, 8, 13).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P2W"));
assertEquals(d1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 20).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftWeek5Fixed() {
ZonedDateTime d1 = LocalDate.of(2021, 4, 24).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P14D"));
assertEquals(d1.getDayOfWeek(), shifted.getDayOfWeek());
ZonedDateTime expected = LocalDate.of(2021, 4, 24).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftDay1Fixed() {
ZonedDateTime d1 = LocalDate.of(2021, 4, 24).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1D"));
ZonedDateTime expected = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftDay2Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 5).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P1D"));
ZonedDateTime expected = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftDay3Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 5).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P75D"));
ZonedDateTime expected = LocalDate.of(2021, 2, 13).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftDay4Fixed() {
ZonedDateTime d1 = LocalDate.of(2018, 1, 5).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("P75D"));
ZonedDateTime expected = LocalDate.of(2021, 4, 19).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftHours1Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 5).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("PT8H"));
ZonedDateTime expected = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
@Test
public void shouldCalcShiftHours2Fixed() {
ZonedDateTime d1 = LocalDate.of(2017, 1, 10).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime d2 = LocalDate.of(2021, 4, 27).atTime(LocalTime.of(12, 47)).atZone(systemDefault());
ZonedDateTime shifted = shiftByMultipleOfPeriod(d1, d2, PeriodDuration.parse("PT8H"));
ZonedDateTime expected = LocalDate.of(2021, 4, 26).atStartOfDay(ZoneId.systemDefault());
assertEquals(shifted.toString(), expected, shifted);
}
}