From 1fc0b198c914a03c684c4b50d43caf0a2666dbb3 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Wed, 3 Sep 2025 21:08:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=B0=86=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E5=8C=BA=E9=97=B4=E8=BD=AC=E4=B8=BA=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=8C=BA=E9=97=B4=E7=9A=84=E6=96=B9=E6=B3=95?= =?UTF-8?q?=20(#64=20@Gitea)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重载 `DateTimeTools#toDateTimeRange` - 对新增的方法进行单元测试 - 简化单元测试代码 --- .../plusone/commons/util/DateTimeTools.java | 52 +++++++- .../commons/util/DateTimeToolsTests.java | 115 ++++++++++++++---- 2 files changed, 141 insertions(+), 26 deletions(-) diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java index e976a3a..137f8d8 100644 --- a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java +++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java @@ -27,11 +27,11 @@ import java.time.Year; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.chrono.IsoChronology; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; +import com.google.common.collect.BoundType; import com.google.common.collect.Range; import xyz.zhouxy.plusone.commons.time.Quarter; @@ -649,7 +649,7 @@ public class DateTimeTools { // ================================ // ================================ - // #region - others + // #region - range // ================================ /** @@ -673,6 +673,52 @@ public class DateTimeTools { return Range.closedOpen(date.atStartOfDay(zone), startOfNextDate(date, zone)); } + /** + * 将指定日期范围转为日期时间范围 + * + * @param dateRange 日期范围 + * @return 对应的日期时间范围 + */ + public static Range toDateTimeRange(Range dateRange) { + BoundType lowerBoundType = dateRange.lowerBoundType(); + LocalDateTime lowerEndpoint = lowerBoundType == BoundType.CLOSED + ? dateRange.lowerEndpoint().atStartOfDay() + : dateRange.lowerEndpoint().plusDays(1).atStartOfDay(); + BoundType upperBoundType = dateRange.upperBoundType(); + LocalDateTime upperEndpoint = upperBoundType == BoundType.CLOSED + ? dateRange.upperEndpoint().plusDays(1).atStartOfDay() + : dateRange.upperEndpoint().atStartOfDay(); + + return Range.closedOpen(lowerEndpoint, upperEndpoint); + } + + /** + * 将指定日期范围转为日期时间范围 + * + * @param dateRange 日期范围 + * @return 对应的日期时间范围 + */ + public static Range toDateTimeRange(Range dateRange, ZoneId zone) { + BoundType lowerBoundType = dateRange.lowerBoundType(); + ZonedDateTime lowerEndpoint = lowerBoundType == BoundType.CLOSED + ? dateRange.lowerEndpoint().atStartOfDay(zone) + : dateRange.lowerEndpoint().plusDays(1).atStartOfDay(zone); + BoundType upperBoundType = dateRange.upperBoundType(); + ZonedDateTime upperEndpoint = upperBoundType == BoundType.CLOSED + ? dateRange.upperEndpoint().plusDays(1).atStartOfDay(zone) + : dateRange.upperEndpoint().atStartOfDay(zone); + + return Range.closedOpen(lowerEndpoint, upperEndpoint); + } + + // ================================ + // #endregion - range + // ================================ + + // ================================ + // #region - others + // ================================ + /** * 判断指定年份是否为闰年 * @@ -680,7 +726,7 @@ public class DateTimeTools { * @return 指定年份是否为闰年 */ public static boolean isLeapYear(int year) { - return IsoChronology.INSTANCE.isLeapYear(year); + return Year.isLeap(year); } // ================================ diff --git a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java index efca387..f9da4ec 100644 --- a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java +++ b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.BoundType; import com.google.common.collect.Range; import xyz.zhouxy.plusone.commons.time.Quarter; @@ -83,24 +84,6 @@ class DateTimeToolsTests { CALENDAR.setTime(DATE); } - // Joda - static final org.joda.time.LocalDateTime JODA_LOCAL_DATE_TIME - = new org.joda.time.LocalDateTime(2024, 12, 29, 12, 58, 30, 333); - static final org.joda.time.LocalDate JODA_LOCAL_DATE = JODA_LOCAL_DATE_TIME.toLocalDate(); - static final org.joda.time.LocalTime JODA_LOCAL_TIME = JODA_LOCAL_DATE_TIME.toLocalTime(); - - // Joda - 2024-12-29 12:58:30.333 SystemDefaultZone - static final org.joda.time.DateTimeZone JODA_SYS_ZONE = org.joda.time.DateTimeZone.getDefault(); - static final org.joda.time.DateTime JODA_DATE_TIME_WITH_SYS_ZONE = JODA_LOCAL_DATE_TIME.toDateTime(JODA_SYS_ZONE); - static final org.joda.time.Instant JODA_INSTANT_WITH_SYS_ZONE = JODA_DATE_TIME_WITH_SYS_ZONE.toInstant(); - static final long JODA_INSTANT_MILLIS = JODA_INSTANT_WITH_SYS_ZONE.getMillis(); - - // Joda - 2024-12-29 12:58:30.333 GMT+04:00 - static final org.joda.time.DateTimeZone JODA_ZONE = org.joda.time.DateTimeZone.forID("GMT+04:00"); - static final org.joda.time.DateTime JODA_DATE_TIME = JODA_LOCAL_DATE_TIME.toDateTime(JODA_ZONE); - static final org.joda.time.Instant JODA_INSTANT = JODA_DATE_TIME.toInstant(); - static final long JODA_MILLIS = JODA_INSTANT.getMillis(); - // ================================ // #region - toDate // ================================ @@ -109,9 +92,7 @@ class DateTimeToolsTests { void toDate_timeMillis() { assertNotEquals(SYS_DATE, DATE); log.info("SYS_DATE: {}, DATE: {}", SYS_DATE, DATE); - assertEquals(SYS_DATE, JODA_DATE_TIME_WITH_SYS_ZONE.toDate()); assertEquals(SYS_DATE, DateTimeTools.toDate(INSTANT_MILLIS)); - assertEquals(DATE, JODA_DATE_TIME.toDate()); assertEquals(DATE, DateTimeTools.toDate(MILLIS)); } @@ -393,11 +374,11 @@ class DateTimeToolsTests { // ================================ // ================================ - // #region - others + // #region - range // ================================ @Test - void startDateTimeRange() { + void toDateTimeRange_specifiedDate() { Range localDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE); assertEquals(LOCAL_DATE.atStartOfDay(), localDateTimeRange.lowerEndpoint()); assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint()); @@ -411,8 +392,96 @@ class DateTimeToolsTests { assertFalse(zonedDateTimeRange.contains(LocalDate.of(2024, 12, 30).atStartOfDay().atZone(SYS_ZONE_ID))); } + @Test + void toDateTimeRange_dateRange_openRange() { + // (2000-01-01..2025-10-01) -> [2000-01-02T00:00..2025-10-01T00:00) + Range dateRange = Range.open( + LocalDate.of(2000, 1, 1), + LocalDate.of(2025, 10, 1)); + + Range localDateTimeRange = DateTimeTools.toDateTimeRange(dateRange); + assertEquals(BoundType.CLOSED, localDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 2, 0, 0), localDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, localDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 1, 0, 0), localDateTimeRange.upperEndpoint()); + log.info(localDateTimeRange.toString()); + + Range zonedDateTimeRange = DateTimeTools.toDateTimeRange(dateRange, ZONE_ID); + assertEquals(BoundType.CLOSED, zonedDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 2, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, zonedDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 1, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.upperEndpoint()); + log.info(zonedDateTimeRange.toString()); + } + + @Test + void toDateTimeRange_dateRange_openClosedRange() { + // (2000-01-01..2025-10-01] -> [2025-01-02T00-00..2025-10-02 00:00) + Range dateRange = Range.openClosed( + LocalDate.of(2000, 1, 1), + LocalDate.of(2025, 10, 1)); + + Range localDateTimeRange = DateTimeTools.toDateTimeRange(dateRange); + assertEquals(BoundType.CLOSED, localDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 2, 0, 0), localDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, localDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 2, 0, 0), localDateTimeRange.upperEndpoint()); + log.info(localDateTimeRange.toString()); + + Range zonedDateTimeRange = DateTimeTools.toDateTimeRange(dateRange, ZONE_ID); + assertEquals(BoundType.CLOSED, zonedDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 2, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, zonedDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 2, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.upperEndpoint()); + log.info(zonedDateTimeRange.toString()); + } + + @Test + void toDateTimeRange_dateRange_closedRange() { + // [2000-01-01..2025-10-01] -> [2025-01-01T00-00..2025-10-02 00:00) + Range dateRange = Range.closed( + LocalDate.of(2000, 1, 1), + LocalDate.of(2025, 10, 1)); + + Range localDateTimeRange = DateTimeTools.toDateTimeRange(dateRange); + assertEquals(BoundType.CLOSED, localDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0), localDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, localDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 2, 0, 0), localDateTimeRange.upperEndpoint()); + log.info(localDateTimeRange.toString()); + + Range zonedDateTimeRange = DateTimeTools.toDateTimeRange(dateRange, ZONE_ID); + assertEquals(BoundType.CLOSED, zonedDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, zonedDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 2, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.upperEndpoint()); + log.info(zonedDateTimeRange.toString()); + } + + @Test + void toDateTimeRange_dateRange_closedOpenRange() { + // [2025-01-01..2025-10-01) -> [2025-01-01T00-00..2025-10-01 00:00) + Range dateRange = Range.closedOpen( + LocalDate.of(2000, 1, 1), + LocalDate.of(2025, 10, 1)); + + Range localDateTimeRange = DateTimeTools.toDateTimeRange(dateRange); + assertEquals(BoundType.CLOSED, localDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0), localDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, localDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 1, 0, 0), localDateTimeRange.upperEndpoint()); + log.info(localDateTimeRange.toString()); + + Range zonedDateTimeRange = DateTimeTools.toDateTimeRange(dateRange, ZONE_ID); + assertEquals(BoundType.CLOSED, zonedDateTimeRange.lowerBoundType()); + assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.lowerEndpoint()); + assertEquals(BoundType.OPEN, zonedDateTimeRange.upperBoundType()); + assertEquals(LocalDateTime.of(2025, 10, 1, 0, 0).atZone(ZONE_ID), zonedDateTimeRange.upperEndpoint()); + log.info(zonedDateTimeRange.toString()); + } + // ================================ - // #endregion - others + // #endregion - range // ================================ // ================================