From aa61452da3635ba7c43c41cf08c5486e8e5f6022 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Mon, 25 Oct 2021 10:07:43 +0330 Subject: [PATCH] Add months distance methods This makes the app free from months count assumption thus making it more flexible for other kinds of calendars. --- .../persiancalendar/calendar/CivilDate.java | 12 ++++++- .../persiancalendar/calendar/IslamicDate.java | 12 ++++++- .../persiancalendar/calendar/PersianDate.java | 12 ++++++- .../calendar/YearMonthDate.java | 32 +++++++++++++++++++ .../persiancalendar/calendar/MainTests.kt | 31 ++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/github/persiancalendar/calendar/YearMonthDate.java diff --git a/src/main/java/io/github/persiancalendar/calendar/CivilDate.java b/src/main/java/io/github/persiancalendar/calendar/CivilDate.java index 7e880cf..d608b60 100644 --- a/src/main/java/io/github/persiancalendar/calendar/CivilDate.java +++ b/src/main/java/io/github/persiancalendar/calendar/CivilDate.java @@ -4,7 +4,7 @@ * @author Amir */ -public class CivilDate extends AbstractDate { +public class CivilDate extends AbstractDate implements YearMonthDate { public CivilDate(int year, int month, int dayOfMonth) { super(year, month, dayOfMonth); @@ -73,4 +73,14 @@ protected int[] fromJdn(long jdn) { } else return julianFromJdn(jdn); } + + @Override + public CivilDate monthStartOfMonthsDistance(int monthsDistance) { + return TwelveMonthsYear.monthStartOfMonthsDistance(this, monthsDistance, CivilDate::new); + } + + @Override + public int monthsDistanceTo(CivilDate date) { + return TwelveMonthsYear.monthsDistanceTo(this, date); + } } diff --git a/src/main/java/io/github/persiancalendar/calendar/IslamicDate.java b/src/main/java/io/github/persiancalendar/calendar/IslamicDate.java index 2696a85..f703f64 100644 --- a/src/main/java/io/github/persiancalendar/calendar/IslamicDate.java +++ b/src/main/java/io/github/persiancalendar/calendar/IslamicDate.java @@ -8,7 +8,7 @@ * @author Amir */ -public class IslamicDate extends AbstractDate { +public class IslamicDate extends AbstractDate implements YearMonthDate { // Converters public static boolean useUmmAlQura = false; @@ -51,4 +51,14 @@ protected int[] fromJdn(long jdn) { return result; } + + @Override + public IslamicDate monthStartOfMonthsDistance(int monthsDistance) { + return TwelveMonthsYear.monthStartOfMonthsDistance(this, monthsDistance, IslamicDate::new); + } + + @Override + public int monthsDistanceTo(IslamicDate date) { + return TwelveMonthsYear.monthsDistanceTo(this, date); + } } diff --git a/src/main/java/io/github/persiancalendar/calendar/PersianDate.java b/src/main/java/io/github/persiancalendar/calendar/PersianDate.java index d6755f8..3dc922d 100644 --- a/src/main/java/io/github/persiancalendar/calendar/PersianDate.java +++ b/src/main/java/io/github/persiancalendar/calendar/PersianDate.java @@ -3,7 +3,7 @@ import io.github.persiancalendar.calendar.persian.AlgorithmicConverter; import io.github.persiancalendar.calendar.persian.LookupTableConverter; -public class PersianDate extends AbstractDate { +public class PersianDate extends AbstractDate implements YearMonthDate { public PersianDate(int year, int month, int dayOfMonth) { super(year, month, dayOfMonth); @@ -29,4 +29,14 @@ protected int[] fromJdn(long jdn) { int[] result = LookupTableConverter.fromJdn(jdn); return result == null ? AlgorithmicConverter.fromJdn(jdn) : result; } + + @Override + public PersianDate monthStartOfMonthsDistance(int monthsDistance) { + return TwelveMonthsYear.monthStartOfMonthsDistance(this, monthsDistance, PersianDate::new); + } + + @Override + public int monthsDistanceTo(PersianDate date) { + return TwelveMonthsYear.monthsDistanceTo(this, date); + } } diff --git a/src/main/java/io/github/persiancalendar/calendar/YearMonthDate.java b/src/main/java/io/github/persiancalendar/calendar/YearMonthDate.java new file mode 100644 index 0000000..5d0df0b --- /dev/null +++ b/src/main/java/io/github/persiancalendar/calendar/YearMonthDate.java @@ -0,0 +1,32 @@ +package io.github.persiancalendar.calendar; + +interface YearMonthDate { + // Ideally getYear()/getMonth()/getDay() also should be moved to this interface + + T monthStartOfMonthsDistance(int monthsDistance); + + int monthsDistanceTo(T date); + + interface CreateDate { + T createDate(int year, int month, int dayOfMonth); + } + + class TwelveMonthsYear { + static T monthStartOfMonthsDistance( + T baseDate, int monthsDistance, CreateDate createDate + ) { + int month = monthsDistance + baseDate.getMonth() - 1; // make it zero based for easier calculations + int year = baseDate.getYear() + month / 12; + month %= 12; + if (month < 0) { + year -= 1; + month += 12; + } + return createDate.createDate(year, month + 1, 1); + } + + static int monthsDistanceTo(T baseDate, T toDate) { + return (toDate.getYear() - baseDate.getYear()) * 12 + toDate.getMonth() - baseDate.getMonth(); + } + } +} diff --git a/src/test/kotlin/io/github/persiancalendar/calendar/MainTests.kt b/src/test/kotlin/io/github/persiancalendar/calendar/MainTests.kt index 498c1b6..067cc01 100644 --- a/src/test/kotlin/io/github/persiancalendar/calendar/MainTests.kt +++ b/src/test/kotlin/io/github/persiancalendar/calendar/MainTests.kt @@ -179,4 +179,35 @@ class MainTests { assertTrue(dayOfMonth == 1 || dayOfMonth == previousDayOfMonth + 1) dayOfMonth }.let {} + + @Test + fun `Test month distance methods`() { + run { + val date = CivilDate(2014, 7, 7) + assertEquals(0, date.monthsDistanceTo(date)) + assertEquals(-10, date.monthsDistanceTo(CivilDate(2013, 9, 5))) + assertEquals(30, date.monthsDistanceTo(CivilDate(2017, 1, 7))) + assertEquals(CivilDate(2013, 12, 1), date.monthStartOfMonthsDistance(-7)) + assertEquals(CivilDate(2014, 6, 1), date.monthStartOfMonthsDistance(-1)) + assertEquals(CivilDate(2014, 7, 1), date.monthStartOfMonthsDistance(0)) + assertEquals(CivilDate(2014, 8, 1), date.monthStartOfMonthsDistance(1)) + assertEquals(CivilDate(2015, 1, 1), date.monthStartOfMonthsDistance(6)) + } + + (-40..40).forEach { + val date = PersianDate(1318, 2, 5) + val dateWithDistance = date.monthStartOfMonthsDistance(it) + assertEquals(it, date.monthsDistanceTo(dateWithDistance)) + } + (-40..40).forEach { + val date = CivilDate(2016, 7, 26) + val dateWithDistance = date.monthStartOfMonthsDistance(it) + assertEquals(it, date.monthsDistanceTo(dateWithDistance)) + } + (-40..40).forEach { + val date = IslamicDate(1440, 7, 26) + val dateWithDistance = date.monthStartOfMonthsDistance(it) + assertEquals(it, date.monthsDistanceTo(dateWithDistance)) + } + } }