diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java index 12edc91..b66f207 100644 --- a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java @@ -11,6 +11,7 @@ import java.time.format.TextStyle; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; +import java.time.temporal.IsoFields; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -21,7 +22,6 @@ * @apiNote Utility class for operations on {@link LocalDate}, {@link Date}, {@link LocalDateTime} * @author shortthirdman * @since 1.0 - * https://howtodoinjava.com/java/date-time/date-validation/ */ public final class DateUtils { @@ -392,4 +392,57 @@ public static String monthShortNameToFullName(String abbreviation) { return monthOptional.orElseThrow(IllegalArgumentException::new) .getDisplayName(TextStyle.FULL, Locale.getDefault()); } + + /** + * Get the number of quarters in between two dates + * @param start the start date + * @param end the end date + * @return the number of quarters + */ + public static Long quarterCount(LocalDate start, LocalDate end) { + if (start == null || end == null) { + throw new IllegalArgumentException("Start date or end date can not be null"); + } + + return IsoFields.QUARTER_YEARS.between(start, end); + } + + /** + * Get the quarter number in a short pattern-style + * @param date the {@link LocalDate} + * @return the quarter in the format "Q{1-4}" + */ + public static String shortQuarterNumber(LocalDate date) { + if (date == null) { + throw new IllegalArgumentException("Date can not be null"); + } + + return date.format(DateTimeFormatter.ofPattern("QQQ", Locale.ENGLISH)); + } + + /** + * Get the quarter number in a long pattern-style + * @param date the {@link LocalDate} + * @return the quarter in the format "n-th quarter" + */ + public static String longQuarterNumber(LocalDate date) { + if (date == null) { + throw new IllegalArgumentException("Date can not be null"); + } + + return date.format(DateTimeFormatter.ofPattern("QQQQ", Locale.ENGLISH)); + } + + /** + * Get quarter number for a given date + * @param date the {@link LocalDate} + * @return quarter number + */ + public static Integer getQuarterNumber(LocalDate date) { + if (date == null) { + throw new IllegalArgumentException("Date can not be null"); + } + + return date.get(IsoFields.QUARTER_OF_YEAR); + } } diff --git a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java index 08e42ea..3be1773 100644 --- a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java +++ b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java @@ -5,10 +5,7 @@ import org.junit.jupiter.api.Test; import java.sql.Timestamp; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -198,4 +195,29 @@ public void givenMonthShortName_convertFullName() { assertEquals("January", DateUtils.monthShortNameToFullName("Jan")); assertThrows(IllegalArgumentException.class, () -> DateUtils.monthShortNameToFullName("Fut")); } + + @Test + public void givenLocalDate_getQuarter() { + LocalDate date = LocalDate.of(2024, Month.FEBRUARY, 19); + assertEquals(1, DateUtils.getQuarterNumber(date)); + assertThrows(IllegalArgumentException.class, () -> DateUtils.getQuarterNumber(null)); + } + + @Test + public void givenLocalDate_getFormattedQuarter() { + LocalDate date = LocalDate.of(2024, Month.FEBRUARY, 19); + assertEquals("Q1", DateUtils.shortQuarterNumber(date)); + assertEquals("1st quarter", DateUtils.longQuarterNumber(date)); + assertThrows(IllegalArgumentException.class, () -> DateUtils.shortQuarterNumber(null)); + assertThrows(IllegalArgumentException.class, () -> DateUtils.longQuarterNumber(null)); + } + + @Test + public void givenStartDate_givenEndDate_getQuarterCount() { + LocalDate start = LocalDate.of(2024, Month.FEBRUARY, 19); + LocalDate end = LocalDate.of(2024, Month.MAY, 5); + assertEquals(1, DateUtils.quarterCount(start, end)); + assertThrows(IllegalArgumentException.class, () -> DateUtils.quarterCount(null, end)); + assertThrows(IllegalArgumentException.class, () -> DateUtils.quarterCount(start, null)); + } }