From 0967b413ac745e454c6ac93b74ce593fa816376b Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Wed, 28 Apr 2021 17:39:18 +0200 Subject: [PATCH] [New] Added PeriodHelper.shiftMonths() --- .../li/strolch/utils/time/PeriodHelper.java | 18 +++ .../strolch/utils/time/PeriodHelperTest.java | 137 ++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/li.strolch.utils/src/main/java/li/strolch/utils/time/PeriodHelper.java b/li.strolch.utils/src/main/java/li/strolch/utils/time/PeriodHelper.java index e1e0446a2..768a5e769 100644 --- a/li.strolch.utils/src/main/java/li/strolch/utils/time/PeriodHelper.java +++ b/li.strolch.utils/src/main/java/li/strolch/utils/time/PeriodHelper.java @@ -28,6 +28,24 @@ public class PeriodHelper { 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. + * + * @param date + * the date to shift + * @param nrOfMonths + * the number of months to shift the given date by + * + * @return the shifted date + */ + public static ZonedDateTime shiftMonths(ZonedDateTime date, int nrOfMonths) { + int selectedDayOfMonth = date.getDayOfMonth(); + ZonedDateTime next = date.plusMonths(nrOfMonths); + 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 diff --git a/li.strolch.utils/src/test/java/li/strolch/utils/time/PeriodHelperTest.java b/li.strolch.utils/src/test/java/li/strolch/utils/time/PeriodHelperTest.java index 7acfa4946..6728fc076 100644 --- a/li.strolch.utils/src/test/java/li/strolch/utils/time/PeriodHelperTest.java +++ b/li.strolch.utils/src/test/java/li/strolch/utils/time/PeriodHelperTest.java @@ -5,6 +5,8 @@ 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; @@ -87,6 +89,141 @@ public class PeriodHelperTest { assertEquals(12, monthsIn(PeriodDuration.parse("P1Y")), 0.0); } + @Test + public void shouldPlusMonthsNormal() { + int expectedDay = 4; + + 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 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; + + 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()); + } + + // 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 shouldPlusMonths30() { + int expectedDay = 30; + + 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 = 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 public void shouldCalcShiftDays1() { ZonedDateTime past = ZonedDateTime.now().minusDays(35);