From d5192d8f61543d88ef866f4bf95ea3e12e3e3353 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 18 Oct 2020 13:23:37 +0800 Subject: [PATCH] add method --- CHANGELOG.md | 2 + .../java/cn/hutool/core/date/ChineseDate.java | 11 +- .../java/cn/hutool/core/date/DateBetween.java | 4 +- .../java/cn/hutool/core/date/DateUnit.java | 101 +++++++++++++++--- .../hutool/core/date/LocalDateTimeUtil.java | 40 ++++++- .../core/date/TemporalAccessorUtil.java | 2 +- .../cn/hutool/core/date/TemporalUtil.java | 41 +++++++ .../hutool/core/date/chinese/LunarInfo.java | 7 +- .../cn/hutool/core/date/ChineseDateTest.java | 10 ++ 9 files changed, 192 insertions(+), 26 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f7084dd..ca9a55c02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * 【core 】 Assert增加函数接口(pr#1166@Github) * 【core 】 新增AtomicIntegerArray、AtomicLongArray转换 * 【extra 】 PinyinUtil新增Bopomofo4j支持 +* 【core 】 新增TemporalUtil工具类,新增时间相关方法 ### Bug修复 * 【core 】 解决农历判断节日未判断大小月导致的问题(issue#I1XHSF@Gitee) @@ -30,6 +31,7 @@ * 【core 】 修复CaseInsensitiveMap的remove等方法并没有忽略大小写的问题(pr#1163@Gitee) * 【poi 】 修复合并单元格值读取错误的问题 * 【poi 】 修复NamedSql解析形如col::numeric出错问题(issue#I1YHBX@Gitee) +* 【core 】 修复计算相差天数导致的问题 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java b/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java index 368cd302d..e29679925 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java @@ -21,7 +21,9 @@ public class ChineseDate { /** * 1900-01-31 */ - private static final long BASE_DATE = -2206425943000L; +// private static final long BASE_DATE = -2206425943000L; + private static final long BASE_DAY = -25538; + //农历年 private final int year; //农历月 @@ -46,13 +48,12 @@ public class ChineseDate { */ public ChineseDate(Date date) { // 求出和1900年1月31日相差的天数 - int offset = (int) ((date.getTime() - BASE_DATE) / DateUnit.DAY.getMillis()); + int offset = (int) ((date.getTime() / DateUnit.DAY.getMillis()) - BASE_DAY); // 计算农历年份 // 用offset减去每农历年的天数,计算当天是农历第几天,offset是当年的第几天 int daysOfYear; int iYear = LunarInfo.BASE_YEAR; - final int maxYear = LunarInfo.getMaxYear(); - for (; iYear <= maxYear; iYear++) { + for (; iYear <= LunarInfo.MAX_YEAR; iYear++) { daysOfYear = LunarInfo.yearDays(iYear); if (offset < daysOfYear) { break; @@ -299,7 +300,7 @@ public class ChineseDate { if (D >= firstNode) { gzM = GanZhi.cyclicalm((Y - LunarInfo.BASE_YEAR) * 12 + M + 12); } - int dayCyclical = (int) ((DateUtil.parseDate(Y + "-" + M + "-" + "1").getTime() - BASE_DATE + 2592000000L) / DateUnit.DAY.getMillis()) + 10; + int dayCyclical = (int) ((DateUtil.parseDate(Y + "-" + M + "-" + "1").getTime() / DateUnit.DAY.getMillis() - BASE_DAY + 30)) + 10; String gzD = GanZhi.cyclicalm(dayCyclical + D - 1); return gzyear + "年" + gzM + "月" + gzD + "日"; } diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateBetween.java b/hutool-core/src/main/java/cn/hutool/core/date/DateBetween.java index a190f5131..7a28fb572 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateBetween.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateBetween.java @@ -26,7 +26,7 @@ public class DateBetween implements Serializable{ * * @param begin 起始时间 * @param end 结束时间 - * @return {@link DateBetween} + * @return DateBetween * @since 3.2.3 */ public static DateBetween create(Date begin, Date end) { @@ -40,7 +40,7 @@ public class DateBetween implements Serializable{ * @param begin 起始时间 * @param end 结束时间 * @param isAbs 日期间隔是否只保留绝对值正数 - * @return {@link DateBetween} + * @return DateBetween * @since 3.2.3 */ public static DateBetween create(Date begin, Date end, boolean isAbs) { diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUnit.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUnit.java index 2f2a58544..019678a96 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateUnit.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUnit.java @@ -1,33 +1,108 @@ package cn.hutool.core.date; +import java.time.temporal.ChronoUnit; + /** * 日期时间单位,每个单位都是以毫秒为基数 - * @author Looly * + * @author Looly */ public enum DateUnit { - /** 一毫秒 */ - MS(1), - /** 一秒的毫秒数 */ - SECOND(1000), - /**一分钟的毫秒数 */ + /** + * 一毫秒 + */ + MS(1), + /** + * 一秒的毫秒数 + */ + SECOND(1000), + /** + * 一分钟的毫秒数 + */ MINUTE(SECOND.getMillis() * 60), - /**一小时的毫秒数 */ + /** + * 一小时的毫秒数 + */ HOUR(MINUTE.getMillis() * 60), - /**一天的毫秒数 */ + /** + * 一天的毫秒数 + */ DAY(HOUR.getMillis() * 24), - /**一周的毫秒数 */ + /** + * 一周的毫秒数 + */ WEEK(DAY.getMillis() * 7); - + private final long millis; - DateUnit(long millis){ + + DateUnit(long millis) { this.millis = millis; } - + /** * @return 单位对应的毫秒数 */ - public long getMillis(){ + public long getMillis() { return this.millis; } + + /** + * 单位兼容转换,将DateUnit转换为对应的{@link ChronoUnit} + * + * @return {@link ChronoUnit} + * @since 5.4.5 + */ + public ChronoUnit toChronoUnit() { + return DateUnit.toChronoUnit(this); + } + + /** + * 单位兼容转换,将{@link ChronoUnit}转换为对应的DateUnit + * + * @param unit {@link ChronoUnit} + * @return DateUnit,null表示不支持此单位 + * @since 5.4.5 + */ + public static DateUnit of(ChronoUnit unit) { + switch (unit) { + case MICROS: + return DateUnit.MS; + case SECONDS: + return DateUnit.SECOND; + case MINUTES: + return DateUnit.MINUTE; + case HOURS: + return DateUnit.HOUR; + case DAYS: + return DateUnit.DAY; + case WEEKS: + return DateUnit.WEEK; + } + return null; + } + + /** + * 单位兼容转换,将DateUnit转换为对应的{@link ChronoUnit} + * + * @param unit DateUnit + * @return {@link ChronoUnit} + * @since 5.4.5 + */ + public static ChronoUnit toChronoUnit(DateUnit unit) { + switch (unit) { + case MS: + return ChronoUnit.MICROS; + case SECOND: + return ChronoUnit.SECONDS; + case MINUTE: + return ChronoUnit.MINUTES; + case HOUR: + return ChronoUnit.HOURS; + case DAY: + return ChronoUnit.DAYS; + case WEEK: + return ChronoUnit.WEEKS; + } + return null; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java index a1745440f..ba08a2cc2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java @@ -9,11 +9,14 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalUnit; import java.util.Date; @@ -412,14 +415,43 @@ public class LocalDateTimeUtil { *

* 返回结果为{@link Duration}对象,通过调用toXXX方法返回相差单位 * - * @param startTime 开始时间 - * @param endTime 结束时间 + * @param startTimeInclude 开始时间(包含) + * @param endTimeExclude 结束时间(不包含) * @return 时间差 {@link Duration}对象 + * @see TemporalUtil#between(Temporal, Temporal) */ - public static Duration between(LocalDateTime startTime, LocalDateTime endTime) { - return Duration.between(startTime, endTime); + public static Duration between(LocalDateTime startTimeInclude, LocalDateTime endTimeExclude) { + return TemporalUtil.between(startTimeInclude, endTimeExclude); } + /** + * 获取两个日期的差,如果结束时间早于开始时间,获取结果为负。 + *

+ * 返回结果为时间差的long值 + * + * @param startTimeInclude 开始时间(包括) + * @param endTimeExclude 结束时间(不包括) + * @param unit 时间差单位 + * @return 时间差 + * @since 5.4.5 + */ + public static long between(LocalDateTime startTimeInclude, LocalDateTime endTimeExclude, ChronoUnit unit) { + return TemporalUtil.between(startTimeInclude, endTimeExclude, unit); + } + + /** + * 获取两个日期的表象时间差,如果结束时间早于开始时间,获取结果为负。 + *

+ * 比如2011年2月1日,和2021年8月11日,日相差了10天,月相差6月 + * + * @param startTimeInclude 开始时间(包括) + * @param endTimeExclude 结束时间(不包括) + * @return 时间差 + * @since 5.4.5 + */ + public static Period betweenPeriod(LocalDate startTimeInclude, LocalDate endTimeExclude) { + return Period.between(startTimeInclude, endTimeExclude); + } /** * 修改为一天的开始时间,例如:2020-02-02 00:00:00,000 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java index d666301cb..3ecb8a5af 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java @@ -20,7 +20,7 @@ import java.time.temporal.TemporalField; * @author looly * @since 5.3.9 */ -public class TemporalAccessorUtil { +public class TemporalAccessorUtil extends TemporalUtil{ /** * 安全获取时间的某个属性,属性不存在返回0 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java new file mode 100644 index 000000000..cd62f4e39 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalUtil.java @@ -0,0 +1,41 @@ +package cn.hutool.core.date; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; + +/** + * {@link Temporal} 工具类封装 + * + * @author looly + * @since 5.4.5 + */ +public class TemporalUtil { + + /** + * 获取两个日期的差,如果结束时间早于开始时间,获取结果为负。 + *

+ * 返回结果为{@link Duration}对象,通过调用toXXX方法返回相差单位 + * + * @param startTimeInclude 开始时间(包含) + * @param endTimeExclude 结束时间(不包含) + * @return 时间差 {@link Duration}对象 + */ + public static Duration between(Temporal startTimeInclude, Temporal endTimeExclude) { + return Duration.between(startTimeInclude, endTimeExclude); + } + + /** + * 获取两个日期的差,如果结束时间早于开始时间,获取结果为负。 + *

+ * 返回结果为时间差的long值 + * + * @param startTimeInclude 开始时间(包括) + * @param endTimeExclude 结束时间(不包括) + * @param unit 时间差单位 + * @return 时间差 + */ + public static long between(Temporal startTimeInclude, Temporal endTimeExclude, ChronoUnit unit) { + return unit.between(startTimeInclude, endTimeExclude); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java b/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java index a088d0c05..12863a4b6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java @@ -40,13 +40,18 @@ public class LunarInfo { 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,//2090-2099 }; + // 支持的最大年限 + public static final int MAX_YEAR = BASE_YEAR + LUNAR_CODE.length - 1; + /** * 获取支持的最大年(包括) * * @return 最大年(包括) + * @deprecated 使用 {@link #MAX_YEAR} */ + @Deprecated public static int getMaxYear() { - return BASE_YEAR + LUNAR_CODE.length - 1; + return MAX_YEAR; } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/date/ChineseDateTest.java b/hutool-core/src/test/java/cn/hutool/core/date/ChineseDateTest.java index 877064d84..2394cfa50 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/ChineseDateTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/ChineseDateTest.java @@ -85,4 +85,14 @@ public class ChineseDateTest { ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("2023-01-20")); Assert.assertTrue(StrUtil.isEmpty(chineseDate.getFestivals())); } + + @Test + public void dateTest(){ + // 修复这两个日期不正确的问题 + // 问题出在计算与1900-01-31相差天数的问题上了,相差天数非整天 + ChineseDate date = new ChineseDate(DateUtil.parseDate("1991-09-14")); + Assert.assertEquals("辛未羊年 八月初七", date.toString()); + date = new ChineseDate(DateUtil.parseDate("1991-09-15")); + Assert.assertEquals("辛未羊年 八月初八", date.toString()); + } }