浏览代码

add CalendarUtil

Looly 5 年之前
父节点
当前提交
4a574e3241

+ 1 - 0
CHANGELOG.md

@@ -24,6 +24,7 @@
 * 【json   】     解析Object中对是否为bean单独判断,而不是直接解析
 * 【core   】     SimHash锁改为StampedLock
 * 【core   】     Singleton改为SimpleCache实现
+* 【core   】     增加CalendarUtil,DateUtil相关方法全部迁移到此
 
 ### Bug修复
 * 【extra  】     修复SpringUtil使用devtools重启报错问题

文件差异内容过多而无法显示
+ 820 - 823
hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java


+ 430 - 0
hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java

@@ -0,0 +1,430 @@
+package cn.hutool.core.date;
+
+import cn.hutool.core.comparator.CompareUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedHashSet;
+
+/**
+ * 针对{@link Calendar} 对象封装工具类
+ *
+ * @author looly
+ * @since 5.3.0
+ */
+public class CalendarUtil {
+
+	/**
+	 * 创建Calendar对象,时间为默认时区的当前时间
+	 *
+	 * @return Calendar对象
+	 * @since 4.6.6
+	 */
+	public static Calendar calendar() {
+		return Calendar.getInstance();
+	}
+
+	/**
+	 * 转换为Calendar对象
+	 *
+	 * @param date 日期对象
+	 * @return Calendar对象
+	 */
+	public static Calendar calendar(Date date) {
+		if (date instanceof DateTime) {
+			return ((DateTime) date).toCalendar();
+		} else {
+			return calendar(date.getTime());
+		}
+	}
+
+	/**
+	 * 转换为Calendar对象
+	 *
+	 * @param millis 时间戳
+	 * @return Calendar对象
+	 */
+	public static Calendar calendar(long millis) {
+		final Calendar cal = Calendar.getInstance();
+		cal.setTimeInMillis(millis);
+		return cal;
+	}
+
+	/**
+	 * 是否为上午
+	 *
+	 * @param calendar {@link Calendar}
+	 * @return 是否为上午
+	 */
+	public static boolean isAM(Calendar calendar) {
+		return Calendar.AM == calendar.get(Calendar.AM_PM);
+	}
+
+	/**
+	 * 是否为下午
+	 *
+	 * @param calendar {@link Calendar}
+	 * @return 是否为下午
+	 */
+	public static boolean isPM(Calendar calendar) {
+		return Calendar.PM == calendar.get(Calendar.AM_PM);
+	}
+
+	/**
+	 * 修改日期为某个时间字段起始时间
+	 *
+	 * @param calendar  {@link Calendar}
+	 * @param dateField 时间字段
+	 * @return 原{@link Calendar}
+	 */
+	public static Calendar truncate(Calendar calendar, DateField dateField) {
+		return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.TRUNCATE);
+	}
+
+	/**
+	 * 修改日期为某个时间字段四舍五入时间
+	 *
+	 * @param calendar  {@link Calendar}
+	 * @param dateField 时间字段
+	 * @return 原{@link Calendar}
+	 */
+	public static Calendar round(Calendar calendar, DateField dateField) {
+		return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.ROUND);
+	}
+
+	/**
+	 * 修改日期为某个时间字段结束时间
+	 *
+	 * @param calendar  {@link Calendar}
+	 * @param dateField 时间字段
+	 * @return 原{@link Calendar}
+	 */
+	public static Calendar ceiling(Calendar calendar, DateField dateField) {
+		return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.CEILING);
+	}
+
+	/**
+	 * 获取秒级别的开始时间,即忽略毫秒部分
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 * @since 4.6.2
+	 */
+	public static Calendar beginOfSecond(Calendar calendar) {
+		return truncate(calendar, DateField.SECOND);
+	}
+
+	/**
+	 * 获取秒级别的结束时间,即毫秒设置为999
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 * @since 4.6.2
+	 */
+	public static Calendar endOfSecond(Calendar calendar) {
+		return ceiling(calendar, DateField.SECOND);
+	}
+
+	/**
+	 * 获取某天的开始时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar beginOfDay(Calendar calendar) {
+		return truncate(calendar, DateField.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 获取某天的结束时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar endOfDay(Calendar calendar) {
+		return ceiling(calendar, DateField.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 获取给定日期当前周的开始时间,周一定为一周的开始时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar beginOfWeek(Calendar calendar) {
+		return beginOfWeek(calendar, true);
+	}
+
+	/**
+	 * 获取给定日期当前周的开始时间
+	 *
+	 * @param calendar           日期 {@link Calendar}
+	 * @param isMondayAsFirstDay 是否周一做为一周的第一天(false表示周日做为第一天)
+	 * @return {@link Calendar}
+	 * @since 3.1.2
+	 */
+	public static Calendar beginOfWeek(Calendar calendar, boolean isMondayAsFirstDay) {
+		calendar.setFirstDayOfWeek(isMondayAsFirstDay ? Calendar.MONDAY : Calendar.SUNDAY);
+		// WEEK_OF_MONTH为上限的字段(不包括),实际调整的为DAY_OF_MONTH
+		return truncate(calendar, DateField.WEEK_OF_MONTH);
+	}
+
+	/**
+	 * 获取某周的结束时间,周日定为一周的结束
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar endOfWeek(Calendar calendar) {
+		return endOfWeek(calendar, true);
+	}
+
+	/**
+	 * 获取某周的结束时间
+	 *
+	 * @param calendar          日期 {@link Calendar}
+	 * @param isSundayAsLastDay 是否周日做为一周的最后一天(false表示周六做为最后一天)
+	 * @return {@link Calendar}
+	 */
+	public static Calendar endOfWeek(Calendar calendar, boolean isSundayAsLastDay) {
+		calendar.setFirstDayOfWeek(isSundayAsLastDay ? Calendar.MONDAY : Calendar.SUNDAY);
+		// WEEK_OF_MONTH为上限的字段(不包括),实际调整的为DAY_OF_MONTH
+		return ceiling(calendar, DateField.WEEK_OF_MONTH);
+	}
+
+	/**
+	 * 获取某月的开始时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar beginOfMonth(Calendar calendar) {
+		return truncate(calendar, DateField.MONTH);
+	}
+
+	/**
+	 * 获取某月的结束时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar endOfMonth(Calendar calendar) {
+		return ceiling(calendar, DateField.MONTH);
+	}
+
+	/**
+	 * 获取某季度的开始时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 * @since 4.1.0
+	 */
+	public static Calendar beginOfQuarter(Calendar calendar) {
+		//noinspection MagicConstant
+		calendar.set(Calendar.MONTH, calendar.get(DateField.MONTH.getValue()) / 3 * 3);
+		calendar.set(Calendar.DAY_OF_MONTH, 1);
+		return beginOfDay(calendar);
+	}
+
+	/**
+	 * 获取某季度的结束时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 * @since 4.1.0
+	 */
+	public static Calendar endOfQuarter(Calendar calendar) {
+		//noinspection MagicConstant
+		calendar.set(Calendar.MONTH, calendar.get(DateField.MONTH.getValue()) / 3 * 3 + 2);
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return endOfDay(calendar);
+	}
+
+	/**
+	 * 获取某年的开始时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar beginOfYear(Calendar calendar) {
+		return truncate(calendar, DateField.YEAR);
+	}
+
+	/**
+	 * 获取某年的结束时间
+	 *
+	 * @param calendar 日期 {@link Calendar}
+	 * @return {@link Calendar}
+	 */
+	public static Calendar endOfYear(Calendar calendar) {
+		return ceiling(calendar, DateField.YEAR);
+	}
+
+	/**
+	 * 比较两个日期是否为同一天
+	 *
+	 * @param cal1 日期1
+	 * @param cal2 日期2
+	 * @return 是否为同一天
+	 */
+	public static boolean isSameDay(Calendar cal1, Calendar cal2) {
+		if (cal1 == null || cal2 == null) {
+			throw new IllegalArgumentException("The date must not be null");
+		}
+		return cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && //
+				cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && //
+				cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA);
+	}
+
+	/**
+	 * 获得指定日期区间内的年份和季节<br>
+	 *
+	 * @param startDate 起始日期(包含)
+	 * @param endDate   结束日期(包含)
+	 * @return 季度列表 ,元素类似于 20132
+	 * @since 4.1.15
+	 */
+	public static LinkedHashSet<String> yearAndQuarter(long startDate, long endDate) {
+		LinkedHashSet<String> quarters = new LinkedHashSet<>();
+		final Calendar cal = calendar(startDate);
+		while (startDate <= endDate) {
+			// 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环
+			quarters.add(yearAndQuarter(cal));
+
+			cal.add(Calendar.MONTH, 3);
+			startDate = cal.getTimeInMillis();
+		}
+
+		return quarters;
+	}
+
+	/**
+	 * 获得指定日期年份和季节<br>
+	 * 格式:[20131]表示2013年第一季度
+	 *
+	 * @param cal 日期
+	 */
+	public static String yearAndQuarter(Calendar cal) {
+		return StrUtil.builder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH) / 3 + 1).toString();
+	}
+
+	/**
+	 * 获取指定日期字段的最小值,例如分钟的最小值是0
+	 *
+	 * @param calendar  {@link Calendar}
+	 * @param dateField {@link DateField}
+	 * @return 字段最小值
+	 * @see Calendar#getActualMinimum(int)
+	 * @since 4.5.7
+	 */
+	public static int getBeginValue(Calendar calendar, int dateField) {
+		if (Calendar.DAY_OF_WEEK == dateField) {
+			return calendar.getFirstDayOfWeek();
+		}
+		return calendar.getActualMinimum(dateField);
+	}
+
+	/**
+	 * 获取指定日期字段的最大值,例如分钟的最大值是59
+	 *
+	 * @param calendar  {@link Calendar}
+	 * @param dateField {@link DateField}
+	 * @return 字段最大值
+	 * @see Calendar#getActualMaximum(int)
+	 * @since 4.5.7
+	 */
+	public static int getEndValue(Calendar calendar, int dateField) {
+		if (Calendar.DAY_OF_WEEK == dateField) {
+			return (calendar.getFirstDayOfWeek() + 6) % 7;
+		}
+		return calendar.getActualMaximum(dateField);
+	}
+
+	/**
+	 * Calendar{@link Instant}对象
+	 *
+	 * @param calendar Date对象
+	 * @return {@link Instant}对象
+	 * @since 5.0.5
+	 */
+	public static Instant toInstant(Calendar calendar) {
+		return null == calendar ? null : calendar.toInstant();
+	}
+
+	/**
+	 * {@link Calendar} 转换为 {@link LocalDateTime},使用系统默认时区
+	 *
+	 * @param calendar {@link Calendar}
+	 * @return {@link LocalDateTime}
+	 * @since 5.0.5
+	 */
+	public static LocalDateTime toLocalDateTime(Calendar calendar) {
+		return LocalDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId());
+	}
+
+	/**
+	 * {@code null}安全的{@link Calendar}比较,{@code null}小于任何日期
+	 *
+	 * @param calendar1 日期1
+	 * @param calendar2 日期2
+	 * @return 比较结果,如果calendar1 &lt; calendar2,返回数小于0,calendar1==calendar2返回0,calendar1 &gt; calendar2 大于0
+	 * @since 4.6.2
+	 */
+	public static int compare(Calendar calendar1, Calendar calendar2) {
+		return CompareUtil.compare(calendar1, calendar2);
+	}
+
+	/**
+	 * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
+	 *
+	 * @param birthday      生日
+	 * @param dateToCompare 需要对比的日期
+	 * @return 年龄
+	 */
+	public static int age(Calendar birthday, Calendar dateToCompare) {
+		return age(birthday.getTimeInMillis(), dateToCompare.getTimeInMillis());
+	}
+
+	/**
+	 * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
+	 *
+	 * @param birthday      生日
+	 * @param dateToCompare 需要对比的日期
+	 * @return 年龄
+	 */
+	protected static int age(long birthday, long dateToCompare) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTimeInMillis(dateToCompare);
+
+		if (cal.before(birthday)) {
+			throw new IllegalArgumentException("Birthday is after dateToCompare!");
+		}
+
+		final int year = cal.get(Calendar.YEAR);
+		final int month = cal.get(Calendar.MONTH);
+		final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
+		final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+
+		cal.setTimeInMillis(birthday);
+		int age = year - cal.get(Calendar.YEAR);
+
+		final int monthBirth = cal.get(Calendar.MONTH);
+		if (month == monthBirth) {
+
+			final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
+			final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+			if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth < dayOfMonthBirth) {
+				// 如果生日在当月,但是未达到生日当天的日期,年龄减一
+				age--;
+			}
+		} else if (month < monthBirth) {
+			// 如果当前月份未达到生日的月份,年龄计算减一
+			age--;
+		}
+
+		return age;
+	}
+}

+ 12 - 395
hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java

@@ -3,10 +3,10 @@ package cn.hutool.core.date;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.comparator.CompareUtil;
 import cn.hutool.core.convert.Convert;
-import cn.hutool.core.date.DateModifier.ModifyType;
 import cn.hutool.core.date.format.DateParser;
 import cn.hutool.core.date.format.DatePrinter;
 import cn.hutool.core.date.format.FastDateFormat;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.lang.PatternPool;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.ReUtil;
@@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit;
  *
  * @author xiaoleilu
  */
-public class DateUtil {
+public class DateUtil extends CalendarUtil {
 
 	/**
 	 * java.util.Date EEE MMM zzz 缩写数组
@@ -130,42 +130,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 创建Calendar对象,时间为默认时区的当前时间
-	 *
-	 * @return Calendar对象
-	 * @since 4.6.6
-	 */
-	public static Calendar calendar() {
-		return Calendar.getInstance();
-	}
-
-	/**
-	 * 转换为Calendar对象
-	 *
-	 * @param date 日期对象
-	 * @return Calendar对象
-	 */
-	public static Calendar calendar(Date date) {
-		if (date instanceof DateTime) {
-			return ((DateTime) date).toCalendar();
-		} else {
-			return calendar(date.getTime());
-		}
-	}
-
-	/**
-	 * 转换为Calendar对象
-	 *
-	 * @param millis 时间戳
-	 * @return Calendar对象
-	 */
-	public static Calendar calendar(long millis) {
-		final Calendar cal = Calendar.getInstance();
-		cal.setTimeInMillis(millis);
-		return cal;
-	}
-
-	/**
 	 * 当前时间的时间戳
 	 *
 	 * @param isNano 是否为高精度时间
@@ -360,17 +324,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 是否为上午
-	 *
-	 * @param calendar {@link Calendar}
-	 * @return 是否为上午
-	 * @since 4.5.7
-	 */
-	public static boolean isAM(Calendar calendar) {
-		return Calendar.AM == calendar.get(Calendar.AM_PM);
-	}
-
-	/**
 	 * 是否为下午
 	 *
 	 * @param date 日期
@@ -490,29 +443,6 @@ public class DateUtil {
 		}
 		return yearAndQuarter(startDate.getTime(), endDate.getTime());
 	}
-
-	/**
-	 * 获得指定日期区间内的年份和季节<br>
-	 *
-	 * @param startDate 起始日期(包含)
-	 * @param endDate   结束日期(包含)
-	 * @return 季度列表 ,元素类似于 20132
-	 * @since 4.1.15
-	 */
-	public static LinkedHashSet<String> yearAndQuarter(long startDate, long endDate) {
-		LinkedHashSet<String> quarters = new LinkedHashSet<>();
-		final Calendar cal = calendar(startDate);
-		while (startDate <= endDate) {
-			// 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环
-			quarters.add(yearAndQuarter(cal));
-
-			cal.add(Calendar.MONTH, 3);
-			startDate = cal.getTimeInMillis();
-		}
-
-		return quarters;
-	}
-
 	// ------------------------------------ Format start ----------------------------------------------
 
 	/**
@@ -987,18 +917,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 修改日期为某个时间字段起始时间
-	 *
-	 * @param calendar  {@link Calendar}
-	 * @param dateField 时间字段
-	 * @return 原{@link Calendar}
-	 * @since 4.5.7
-	 */
-	public static Calendar truncate(Calendar calendar, DateField dateField) {
-		return DateModifier.modify(calendar, dateField.getValue(), ModifyType.TRUNCATE);
-	}
-
-	/**
 	 * 修改日期为某个时间字段四舍五入时间
 	 *
 	 * @param date      {@link Date}
@@ -1011,18 +929,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 修改日期为某个时间字段四舍五入时间
-	 *
-	 * @param calendar  {@link Calendar}
-	 * @param dateField 时间字段
-	 * @return 原{@link Calendar}
-	 * @since 4.5.7
-	 */
-	public static Calendar round(Calendar calendar, DateField dateField) {
-		return DateModifier.modify(calendar, dateField.getValue(), ModifyType.ROUND);
-	}
-
-	/**
 	 * 修改日期为某个时间字段结束时间
 	 *
 	 * @param date      {@link Date}
@@ -1035,18 +941,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 修改日期为某个时间字段结束时间
-	 *
-	 * @param calendar  {@link Calendar}
-	 * @param dateField 时间字段
-	 * @return 原{@link Calendar}
-	 * @since 4.5.7
-	 */
-	public static Calendar ceiling(Calendar calendar, DateField dateField) {
-		return DateModifier.modify(calendar, dateField.getValue(), ModifyType.CEILING);
-	}
-
-	/**
 	 * 获取秒级别的开始时间,即忽略毫秒部分
 	 *
 	 * @param date 日期
@@ -1069,28 +963,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取秒级别的开始时间,即忽略毫秒部分
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 * @since 4.6.2
-	 */
-	public static Calendar beginOfSecond(Calendar calendar) {
-		return truncate(calendar, DateField.SECOND);
-	}
-
-	/**
-	 * 获取秒级别的结束时间,即毫秒设置为999
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 * @since 4.6.2
-	 */
-	public static Calendar endOfSecond(Calendar calendar) {
-		return ceiling(calendar, DateField.SECOND);
-	}
-
-	/**
 	 * 获取某天的开始时间
 	 *
 	 * @param date 日期
@@ -1111,26 +983,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取某天的开始时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar beginOfDay(Calendar calendar) {
-		return truncate(calendar, DateField.DAY_OF_MONTH);
-	}
-
-	/**
-	 * 获取某天的结束时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar endOfDay(Calendar calendar) {
-		return ceiling(calendar, DateField.DAY_OF_MONTH);
-	}
-
-	/**
 	 * 获取某周的开始时间,周一定为一周的开始时间
 	 *
 	 * @param date 日期
@@ -1151,54 +1003,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取给定日期当前周的开始时间,周一定为一周的开始时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar beginOfWeek(Calendar calendar) {
-		return beginOfWeek(calendar, true);
-	}
-
-	/**
-	 * 获取给定日期当前周的开始时间
-	 *
-	 * @param calendar           日期 {@link Calendar}
-	 * @param isMondayAsFirstDay 是否周一做为一周的第一天(false表示周日做为第一天)
-	 * @return {@link Calendar}
-	 * @since 3.1.2
-	 */
-	public static Calendar beginOfWeek(Calendar calendar, boolean isMondayAsFirstDay) {
-		calendar.setFirstDayOfWeek(isMondayAsFirstDay ? Calendar.MONDAY : Calendar.SUNDAY);
-		// WEEK_OF_MONTH为上限的字段(不包括),实际调整的为DAY_OF_MONTH
-		return truncate(calendar, DateField.WEEK_OF_MONTH);
-	}
-
-	/**
-	 * 获取某周的结束时间,周日定为一周的结束
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar endOfWeek(Calendar calendar) {
-		return endOfWeek(calendar, true);
-	}
-
-	/**
-	 * 获取某周的结束时间
-	 *
-	 * @param calendar          日期 {@link Calendar}
-	 * @param isSundayAsLastDay 是否周日做为一周的最后一天(false表示周六做为最后一天)
-	 * @return {@link Calendar}
-	 * @since 3.1.2
-	 */
-	public static Calendar endOfWeek(Calendar calendar, boolean isSundayAsLastDay) {
-		calendar.setFirstDayOfWeek(isSundayAsLastDay ? Calendar.MONDAY : Calendar.SUNDAY);
-		// WEEK_OF_MONTH为上限的字段(不包括),实际调整的为DAY_OF_MONTH
-		return ceiling(calendar, DateField.WEEK_OF_MONTH);
-	}
-
-	/**
 	 * 获取某月的开始时间
 	 *
 	 * @param date 日期
@@ -1219,26 +1023,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取某月的开始时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar beginOfMonth(Calendar calendar) {
-		return truncate(calendar, DateField.MONTH);
-	}
-
-	/**
-	 * 获取某月的结束时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar endOfMonth(Calendar calendar) {
-		return ceiling(calendar, DateField.MONTH);
-	}
-
-	/**
 	 * 获取某季度的开始时间
 	 *
 	 * @param date 日期
@@ -1259,34 +1043,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取某季度的开始时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 * @since 4.1.0
-	 */
-	public static Calendar beginOfQuarter(Calendar calendar) {
-		//noinspection MagicConstant
-		calendar.set(Calendar.MONTH, calendar.get(DateField.MONTH.getValue()) / 3 * 3);
-		calendar.set(Calendar.DAY_OF_MONTH, 1);
-		return beginOfDay(calendar);
-	}
-
-	/**
-	 * 获取某季度的结束时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 * @since 4.1.0
-	 */
-	public static Calendar endOfQuarter(Calendar calendar) {
-		//noinspection MagicConstant
-		calendar.set(Calendar.MONTH, calendar.get(DateField.MONTH.getValue()) / 3 * 3 + 2);
-		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
-		return endOfDay(calendar);
-	}
-
-	/**
 	 * 获取某年的开始时间
 	 *
 	 * @param date 日期
@@ -1305,27 +1061,6 @@ public class DateUtil {
 	public static DateTime endOfYear(Date date) {
 		return new DateTime(endOfYear(calendar(date)));
 	}
-
-	/**
-	 * 获取某年的开始时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar beginOfYear(Calendar calendar) {
-		return truncate(calendar, DateField.YEAR);
-	}
-
-	/**
-	 * 获取某年的结束时间
-	 *
-	 * @param calendar 日期 {@link Calendar}
-	 * @return {@link Calendar}
-	 */
-	public static Calendar endOfYear(Calendar calendar) {
-		return ceiling(calendar, DateField.YEAR);
-	}
-
 	// --------------------------------------------------- Offset for now
 
 	/**
@@ -1554,9 +1289,9 @@ public class DateUtil {
 	/**
 	 * 计算指定指定时间区间内的周数
 	 *
-	 * @param beginDate   开始时间
-	 * @param endDate     结束时间
-	 * @param isReset 是否重置时间为起始时间
+	 * @param beginDate 开始时间
+	 * @param endDate   结束时间
+	 * @param isReset   是否重置时间为起始时间
 	 * @return 周数
 	 */
 	public static long betweenWeek(Date beginDate, Date endDate, boolean isReset) {
@@ -1687,23 +1422,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 比较两个日期是否为同一天
-	 *
-	 * @param cal1 日期1
-	 * @param cal2 日期2
-	 * @return 是否为同一天
-	 * @since 4.1.13
-	 */
-	public static boolean isSameDay(Calendar cal1, Calendar cal2) {
-		if (cal1 == null || cal2 == null) {
-			throw new IllegalArgumentException("The date must not be null");
-		}
-		return cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && //
-				cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && //
-				cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA);
-	}
-
-	/**
 	 * 计时,常用于记录某段代码的执行时间,单位:纳秒
 	 *
 	 * @param preTime 之前记录的时间
@@ -1863,41 +1581,16 @@ public class DateUtil {
 	/**
 	 * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
 	 *
-	 * @param birthDay      生日
+	 * @param birthday      生日
 	 * @param dateToCompare 需要对比的日期
 	 * @return 年龄
 	 */
-	public static int age(Date birthDay, Date dateToCompare) {
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(dateToCompare);
-
-		if (cal.before(birthDay)) {
-			throw new IllegalArgumentException(StrUtil.format("Birthday is after date {}!", formatDate(dateToCompare)));
-		}
-
-		final int year = cal.get(Calendar.YEAR);
-		final int month = cal.get(Calendar.MONTH);
-		final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
-		final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
-
-		cal.setTime(birthDay);
-		int age = year - cal.get(Calendar.YEAR);
-
-		final int monthBirth = cal.get(Calendar.MONTH);
-		if (month == monthBirth) {
-
-			final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
-			final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
-			if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth < dayOfMonthBirth) {
-				// 如果生日在当月,但是未达到生日当天的日期,年龄减一
-				age--;
-			}
-		} else if (month < monthBirth) {
-			// 如果当前月份未达到生日的月份,年龄计算减一
-			age--;
+	public static int age(Date birthday, Date dateToCompare) {
+		Assert.notNull(birthday, "Birthday can not be null !");
+		if (null == dateToCompare) {
+			dateToCompare = date();
 		}
-
-		return age;
+		return age(birthday.getTime(), dateToCompare.getTime());
 	}
 
 	/**
@@ -2043,38 +1736,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * 获取指定日期字段的最小值,例如分钟的最小值是0
-	 *
-	 * @param calendar  {@link Calendar}
-	 * @param dateField {@link DateField}
-	 * @return 字段最小值
-	 * @see Calendar#getActualMinimum(int)
-	 * @since 4.5.7
-	 */
-	public static int getBeginValue(Calendar calendar, int dateField) {
-		if (Calendar.DAY_OF_WEEK == dateField) {
-			return calendar.getFirstDayOfWeek();
-		}
-		return calendar.getActualMinimum(dateField);
-	}
-
-	/**
-	 * 获取指定日期字段的最大值,例如分钟的最大值是59
-	 *
-	 * @param calendar  {@link Calendar}
-	 * @param dateField {@link DateField}
-	 * @return 字段最大值
-	 * @see Calendar#getActualMaximum(int)
-	 * @since 4.5.7
-	 */
-	public static int getEndValue(Calendar calendar, int dateField) {
-		if (Calendar.DAY_OF_WEEK == dateField) {
-			return (calendar.getFirstDayOfWeek() + 6) % 7;
-		}
-		return calendar.getActualMaximum(dateField);
-	}
-
-	/**
 	 * {@code null}安全的日期比较,{@code null}对象排在末尾
 	 *
 	 * @param date1 日期1
@@ -2087,18 +1748,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * {@code null}安全的{@link Calendar}比较,{@code null}小于任何日期
-	 *
-	 * @param calendar1 日期1
-	 * @param calendar2 日期2
-	 * @return 比较结果,如果calendar1 &lt; calendar2,返回数小于0,calendar1==calendar2返回0,calendar1 &gt; calendar2 大于0
-	 * @since 4.6.2
-	 */
-	public static int compare(Calendar calendar1, Calendar calendar2) {
-		return CompareUtil.compare(calendar1, calendar2);
-	}
-
-	/**
 	 * 纳秒转毫秒
 	 *
 	 * @param duration 时长
@@ -2132,17 +1781,6 @@ public class DateUtil {
 	}
 
 	/**
-	 * Calendar{@link Instant}对象
-	 *
-	 * @param calendar Date对象
-	 * @return {@link Instant}对象
-	 * @since 5.0.5
-	 */
-	public static Instant toInstant(Calendar calendar) {
-		return null == calendar ? null : calendar.toInstant();
-	}
-
-	/**
 	 * Date对象转换为{@link Instant}对象
 	 *
 	 * @param temporalAccessor Date对象
@@ -2190,20 +1828,9 @@ public class DateUtil {
 	}
 
 	/**
-	 * {@link Calendar} 转换为 {@link LocalDateTime},使用系统默认时区
-	 *
-	 * @param calendar {@link Calendar}
-	 * @return {@link LocalDateTime}
-	 * @since 5.0.5
-	 */
-	public static LocalDateTime toLocalDateTime(Calendar calendar) {
-		return LocalDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId());
-	}
-
-	/**
 	 * {@link Date} 转换为 {@link LocalDateTime},使用系统默认时区
 	 *
-	 * @param date {@link Calendar}
+	 * @param date {@link Date}
 	 * @return {@link LocalDateTime}
 	 * @since 5.0.5
 	 */
@@ -2215,16 +1842,6 @@ public class DateUtil {
 	// ------------------------------------------------------------------------ Private method start
 
 	/**
-	 * 获得指定日期年份和季节<br>
-	 * 格式:[20131]表示2013年第一季度
-	 *
-	 * @param cal 日期
-	 */
-	private static String yearAndQuarter(Calendar cal) {
-		return StrUtil.builder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH) / 3 + 1).toString();
-	}
-
-	/**
 	 * 标准化日期,默认处理以空格区分的日期时间格式,空格前为日期,空格后为时间:<br>
 	 * 将以下字符替换为"-"
 	 *

+ 17 - 1
hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java

@@ -329,28 +329,36 @@ public class DateUtilTest {
 		Assert.assertEquals("20190321", ymd);
 	}
 
-	@SuppressWarnings("ConstantConditions")
 	@Test
 	public void parseTest5() {
 		// 测试时间解析
+		//noinspection ConstantConditions
 		String time = DateUtil.parse("22:12:12").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("22:12:12", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("2:12:12").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("02:12:12", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("2:2:12").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("02:02:12", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("2:2:1").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("02:02:01", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("22:2:1").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("22:02:01", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("2:22:1").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("02:22:01", time);
 
 		// 测试两位时间解析
+		//noinspection ConstantConditions
 		time = DateUtil.parse("2:22").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("02:22:00", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("12:22").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("12:22:00", time);
+		//noinspection ConstantConditions
 		time = DateUtil.parse("12:2").toString(DatePattern.NORM_TIME_FORMAT);
 		Assert.assertEquals("12:02:00", time);
 
@@ -688,6 +696,14 @@ public class DateUtilTest {
 		Assert.assertEquals(18, age);
 	}
 
+	@Test(expected = IllegalArgumentException.class)
+	public void ageTest2(){
+		String d1 = "2019-02-29";
+		String d2 = "2018-02-28";
+		final int age = DateUtil.age(DateUtil.parseDate(d1), DateUtil.parseDate(d2));
+		Assert.assertEquals(18, age);
+	}
+
 	@Test
 	public void isExpiredTest(){
 		DateTime startDate = DateUtil.parse("2019-12-01 17:02:30");