diff --git a/CHANGELOG.md b/CHANGELOG.md index a7f2434c3..7e0dd7f84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ # 5.7.5 (2021-07-12) ### 🐣新特性 +* 【core 】 DateUtil增加ceiling重载,可选是否归零毫秒 ### 🐞Bug修复 * 【core 】 修复FileUtil.normalize处理上级路径的问题(issue#I3YPEH@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java index 57fbfbb08..001c1b11c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java @@ -113,6 +113,23 @@ public class CalendarUtil { return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.CEILING); } + /** + * 修改日期为某个时间字段结束时间
+ * 可选是否归零毫秒。 + * + *

+ * 有时候由于毫秒部分必须为0(如MySQL数据库中),因此在此加上选项。 + *

+ * + * @param calendar {@link Calendar} + * @param dateField 时间字段 + * @param truncateMillisecond 是否毫秒归零 + * @return 原{@link Calendar} + */ + public static Calendar ceiling(Calendar calendar, DateField dateField, boolean truncateMillisecond) { + return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.CEILING, truncateMillisecond); + } + /** * 获取秒级别的开始时间,即忽略毫秒部分 * @@ -655,9 +672,9 @@ public class CalendarUtil { calendar.setLenient(lenient); for (final String parsePattern : parsePatterns) { - if(GlobalCustomFormat.isCustomFormat(parsePattern)){ + if (GlobalCustomFormat.isCustomFormat(parsePattern)) { final Date parse = GlobalCustomFormat.parse(str, parsePattern); - if(null == parse){ + if (null == parse) { continue; } calendar.setTime(parse); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateModifier.java b/hutool-core/src/main/java/cn/hutool/core/date/DateModifier.java index f5c14facf..0fa96638d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateModifier.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateModifier.java @@ -1,9 +1,9 @@ package cn.hutool.core.date; -import java.util.Calendar; - import cn.hutool.core.util.ArrayUtil; +import java.util.Calendar; + /** * 日期修改器
* 用于实现自定义某个日期字段的调整,包括: @@ -15,12 +15,13 @@ import cn.hutool.core.util.ArrayUtil; * * * @author looly - * */ public class DateModifier { - /** 忽略的计算的字段 */ - private static final int[] IGNORE_FIELDS = new int[] { // + /** + * 忽略的计算的字段 + */ + private static final int[] IGNORE_FIELDS = new int[]{ // Calendar.HOUR_OF_DAY, // 与HOUR同名 Calendar.AM_PM, // 此字段单独处理,不参与计算起始和结束 Calendar.DAY_OF_WEEK_IN_MONTH, // 不参与计算 @@ -32,36 +33,58 @@ public class DateModifier { /** * 修改日期 * - * @param calendar {@link Calendar} - * @param dateField 日期字段,即保留到哪个日期字段 + * @param calendar {@link Calendar} + * @param dateField 日期字段,即保留到哪个日期字段 * @param modifyType 修改类型,包括舍去、四舍五入、进一等 * @return 修改后的{@link Calendar} */ public static Calendar modify(Calendar calendar, int dateField, ModifyType modifyType) { + return modify(calendar, dateField, modifyType, false); + } + + /** + * 修改日期,取起始值或者结束值
+ * 可选是否归零毫秒。 + * + *

+ * 在{@link ModifyType#TRUNCATE}模式下,毫秒始终要归零, + * 但是在{@link ModifyType#CEILING}和{@link ModifyType#ROUND}模式下, + * 有时候由于毫秒部分必须为0(如MySQL数据库中),因此在此加上选项。 + *

+ * + * @param calendar {@link Calendar} + * @param dateField 日期字段,即保留到哪个日期字段 + * @param modifyType 修改类型,包括舍去、四舍五入、进一等 + * @param truncateMillisecond 是否归零毫秒 + * @return 修改后的{@link Calendar} + * @since 5.7.5 + */ + public static Calendar modify(Calendar calendar, int dateField, ModifyType modifyType, boolean truncateMillisecond) { // AM_PM上下午特殊处理 if (Calendar.AM_PM == dateField) { boolean isAM = DateUtil.isAM(calendar); switch (modifyType) { - case TRUNCATE: - calendar.set(Calendar.HOUR_OF_DAY, isAM ? 0 : 12); - break; - case CEILING: - calendar.set(Calendar.HOUR_OF_DAY, isAM ? 11 : 23); - break; - case ROUND: - int min = isAM ? 0 : 12; - int max = isAM ? 11 : 23; - int href = (max - min) / 2 + 1; - int value = calendar.get(Calendar.HOUR_OF_DAY); - calendar.set(Calendar.HOUR_OF_DAY, (value < href) ? min : max); - break; + case TRUNCATE: + calendar.set(Calendar.HOUR_OF_DAY, isAM ? 0 : 12); + break; + case CEILING: + calendar.set(Calendar.HOUR_OF_DAY, isAM ? 11 : 23); + break; + case ROUND: + int min = isAM ? 0 : 12; + int max = isAM ? 11 : 23; + int href = (max - min) / 2 + 1; + int value = calendar.get(Calendar.HOUR_OF_DAY); + calendar.set(Calendar.HOUR_OF_DAY, (value < href) ? min : max); + break; } // 处理下一级别字段 return modify(calendar, dateField + 1, modifyType); } + final int endField = truncateMillisecond ? Calendar.SECOND : Calendar.MILLISECOND; // 循环处理各级字段,精确到毫秒字段 - for (int i = dateField + 1; i <= Calendar.MILLISECOND; i++) { + for (int i = dateField + 1; i <= endField; i++) { if (ArrayUtil.contains(IGNORE_FIELDS, i)) { // 忽略无关字段(WEEK_OF_MONTH)始终不做修改 continue; @@ -81,15 +104,21 @@ public class DateModifier { modifyField(calendar, i, modifyType); } + + if (truncateMillisecond) { + calendar.set(Calendar.MILLISECOND, 0); + } + return calendar; } // -------------------------------------------------------------------------------------------------- Private method start + /** * 修改日期字段值 * - * @param calendar {@link Calendar} - * @param field 字段,见{@link Calendar} + * @param calendar {@link Calendar} + * @param field 字段,见{@link Calendar} * @param modifyType {@link ModifyType} */ private static void modifyField(Calendar calendar, int field, ModifyType modifyType) { @@ -99,25 +128,25 @@ public class DateModifier { } switch (modifyType) { - case TRUNCATE: - calendar.set(field, DateUtil.getBeginValue(calendar, field)); - break; - case CEILING: - calendar.set(field, DateUtil.getEndValue(calendar, field)); - break; - case ROUND: - int min = DateUtil.getBeginValue(calendar, field); - int max = DateUtil.getEndValue(calendar, field); - int href; - if (Calendar.DAY_OF_WEEK == field) { - // 星期特殊处理,假设周一是第一天,中间的为周四 - href = (min + 3) % 7; - } else { - href = (max - min) / 2 + 1; - } - int value = calendar.get(field); - calendar.set(field, (value < href) ? min : max); - break; + case TRUNCATE: + calendar.set(field, DateUtil.getBeginValue(calendar, field)); + break; + case CEILING: + calendar.set(field, DateUtil.getEndValue(calendar, field)); + break; + case ROUND: + int min = DateUtil.getBeginValue(calendar, field); + int max = DateUtil.getEndValue(calendar, field); + int href; + if (Calendar.DAY_OF_WEEK == field) { + // 星期特殊处理,假设周一是第一天,中间的为周四 + href = (min + 3) % 7; + } else { + href = (max - min) / 2 + 1; + } + int value = calendar.get(field); + calendar.set(field, (value < href) ? min : max); + break; } // Console.log("# {} -> {}", DateField.of(field), calendar.get(field)); } @@ -127,7 +156,6 @@ public class DateModifier { * 修改类型 * * @author looly - * */ public enum ModifyType { /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java index 1d80f3a3a..a81d2af2a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java @@ -490,7 +490,7 @@ public class DateUtil extends CalendarUtil { } // 检查自定义格式 - if(GlobalCustomFormat.isCustomFormat(format)){ + if (GlobalCustomFormat.isCustomFormat(format)) { return GlobalCustomFormat.format(date, format); } @@ -702,7 +702,7 @@ public class DateUtil extends CalendarUtil { * @since 4.5.18 */ public static DateTime parse(CharSequence dateStr, String format, Locale locale) { - if(GlobalCustomFormat.isCustomFormat(format)){ + if (GlobalCustomFormat.isCustomFormat(format)) { // 自定义格式化器忽略Locale return new DateTime(GlobalCustomFormat.parse(dateStr, format)); } @@ -828,7 +828,7 @@ public class DateUtil extends CalendarUtil { } else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) { // 格式类似:2018-09-13T05:34:31 return parse(utcString, DatePattern.UTC_SIMPLE_FORMAT); - } else if (StrUtil.contains(utcString, CharUtil.DOT)){ + } else if (StrUtil.contains(utcString, CharUtil.DOT)) { // 可能为: 2021-03-17T06:31:33.99 return parse(utcString, DatePattern.UTC_SIMPLE_MS_FORMAT); } @@ -983,7 +983,25 @@ public class DateUtil extends CalendarUtil { } /** - * 获取秒级别的开始时间,即忽略毫秒部分 + * 修改日期为某个时间字段结束时间
+ * 可选是否归零毫秒。 + * + *

+ * 有时候由于毫秒部分必须为0(如MySQL数据库中),因此在此加上选项。 + *

+ * + * @param date {@link Date} + * @param dateField 时间字段 + * @param truncateMillisecond 是否毫秒归零 + * @return {@link DateTime} + * @since 4.5.7 + */ + public static DateTime ceiling(Date date, DateField dateField, boolean truncateMillisecond) { + return new DateTime(ceiling(calendar(date), dateField, truncateMillisecond)); + } + + /** + * 获取秒级别的开始时间,即毫秒部分设置为0 * * @param date 日期 * @return {@link DateTime} @@ -1847,13 +1865,12 @@ public class DateUtil extends CalendarUtil { /** * {@code null}安全的日期比较,并只比较指定格式; {@code null}对象排在末尾, 并指定日期格式; * - * - * @param date1 日期1 - * @param date2 日期2 + * @param date1 日期1 + * @param date2 日期2 * @param format 日期格式,常用格式见: {@link DatePattern}; 允许为空; date1 date2; eg: yyyy-MM-dd * @return 比较结果,如果date1 < date2,返回数小于0,date1==date2返回0,date1 > date2 大于0 - * @since 5.6.4 * @author dazer + * @since 5.6.4 */ public static int compare(Date date1, Date date2, String format) { if (format != null) { diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java index 157049b8f..d80795466 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java @@ -122,6 +122,32 @@ public class DateUtilTest { Assert.assertEquals("2020-02-29 12:59:00", dateTime.toString()); } + @Test + public void ceilingMinuteTest(){ + String dateStr2 = "2020-02-29 12:59:34"; + Date date2 = DateUtil.parse(dateStr2); + + + DateTime dateTime = DateUtil.ceiling(date2, DateField.MINUTE); + Assert.assertEquals("2020-02-29 12:59:59.999", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN)); + + dateTime = DateUtil.ceiling(date2, DateField.MINUTE, true); + Assert.assertEquals("2020-02-29 12:59:59.000", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN)); + } + + @Test + public void ceilingDayTest(){ + String dateStr2 = "2020-02-29 12:59:34"; + Date date2 = DateUtil.parse(dateStr2); + + + DateTime dateTime = DateUtil.ceiling(date2, DateField.DAY_OF_MONTH); + Assert.assertEquals("2020-02-29 23:59:59.999", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN)); + + dateTime = DateUtil.ceiling(date2, DateField.DAY_OF_MONTH, true); + Assert.assertEquals("2020-02-29 23:59:59.000", dateTime.toString(DatePattern.NORM_DATETIME_MS_PATTERN)); + } + @Test public void beginOfWeekTest() { String dateStr = "2017-03-01 22:33:23";