1. 概述
在本篇短小精悍的教程中,我们将探讨如何判断两个 java.util.Date
对象是否表示同一天。
我们会先从 Java 核心 API 的方式入手,重点介绍 Java 8 引入的新特性,再回溯一些 Java 8 之前的传统做法。最后,还会看看几个主流第三方库的解决方案:Apache Commons Lang、Joda-Time 和 Date4J。
✅ 目标很明确:忽略时间部分,只比较年月日。
2. 使用核心 Java
Date
类表示的是一个精确到毫秒的时间点。要判断两个 Date
是否是同一天,关键在于剥离时间信息,只保留年-月-日进行比较。
2.1. 使用 LocalDate
(推荐)
Java 8 引入了全新的日期时间 API(JSR-310),其中 LocalDate
是一个不可变的日期对象,不包含时间与时区信息,非常适合这种场景。
思路很简单:把 Date
转成 LocalDate
,然后直接比较是否相等。
public static boolean isSameDay(Date date1, Date date2) {
LocalDate localDate1 = date1.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalData();
LocalDate localDate2 = date2.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
return localDate1.isEqual(localDate2);
}
⚠️ 注意:toInstant()
将 Date
转为 Instant
,再通过 atZone()
结合系统默认时区转为 LocalDate
。时区会影响“当天”的判定,比如跨时区可能差一天,这点在分布式系统中要特别注意。
✅ 优点:语义清晰,代码简洁,Java 8+ 项目首选。
2.2. 使用 Instant
直接截断
既然 Instant
表示的是时间线上的一个瞬时点,我们也可以直接对它动手——按天为单位截断(truncated),把时分秒全部归零,再比较。
public static boolean isSameDayUsingInstant(Date date1, Date date2) {
Instant instant1 = date1.toInstant()
.truncatedTo(ChronoUnit.DAYS);
Instant instant2 = date2.toInstant()
.truncatedTo(ChronoUnit.DAYS);
return instant1.equals(instant2);
}
✅ 优点:不依赖时区转换,纯粹基于 UTC 时间线操作,避免了时区带来的坑。
❌ 缺点:如果你的业务逻辑是基于“本地日期”(比如用户所在时区的“今天”),这种方式可能不符合预期。
2.3. 使用 SimpleDateFormat
(简单粗暴)
老派但有效的方法:格式化成“yyyyMMdd”字符串,然后字符串比较。
public static boolean isSameDay(Date date1, Date date2) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));
}
✅ 优点:代码极简,兼容性好,Java 1.1 就有。
⚠️ 注意:
SimpleDateFormat
不是线程安全的!高并发场景下记得用ThreadLocal
包装或改用DateTimeFormatter
。- 性能略低,频繁调用建议缓存格式化对象(但注意线程安全)。
适合小项目或临时脚本,不推荐在核心服务中高频使用。
2.4. 使用 Calendar
(传统方式)
Calendar
是 Java 早期处理日期的主力类,虽然笨重但功能全。
思路:分别提取年、月、日字段,逐个比较。
public static boolean isSameDay(Date date1, Date date2) {
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(date1);
Calendar calendar2 = Calendar.getInstance();
calendar2.setTime(date2);
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)
&& calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH)
&& calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
}
⚠️ 注意:
Calendar.MONTH
是从 0 开始的(0 = 一月),虽然这里只是比较,不影响结果,但写业务逻辑时容易踩坑。- 代码啰嗦,可读性一般,
Calendar
已被官方标记为 legacy。
✅ 仅用于维护老系统,新项目建议绕行。
3. 使用外部库
第三方库往往封装得更优雅,减少样板代码。
3.1. Apache Commons Lang DateUtils
DateUtils
是 Java 日期操作的瑞士军刀,尤其适合还在用 Date
/Calendar
的老项目。
Maven 依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
直接调用:
DateUtils.isSameDay(date1, date2);
✅ 优点:
- 一行代码解决,清晰明了
- 内部实现健壮,处理了
null
等边界情况 - 社区广泛使用,可靠性高
强烈推荐在非 Java 8 项目或需要快速交付的场景中使用。
3.2. Joda-Time 库
Joda-Time 是 Java 8 之前事实上的日期标准库,设计优雅,直接影响了 JSR-310。
Maven 依赖:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.12.5</version>
</dependency>
用法也很直观:
public static boolean isSameDay(Date date1, Date date2) {
org.joda.time.LocalDate localDate1 = new org.joda.time.LocalDate(date1);
org.joda.time.LocalDate localDate2 = new org.joda.time.LocalDate(date2);
return localDate1.equals(localDate2);
}
✅ 优点:
- API 设计优秀,类型安全
LocalDate
语义清晰
⚠️ 注意:Java 8+ 项目建议直接用 java.time
,Joda-Time 已进入维护模式,新项目不推荐引入。
3.3. Date4J 库
Date4J 是一个轻量级的日期库,API 简洁。
Maven 依赖:
<dependency>
<groupId>com.darwinsys</groupId>
<artifactId>hirondelle-date4j</artifactId>
<version>1.5.1</version>
</dependency>
使用方式:
public static boolean isSameDay(Date date1, Date date2) {
DateTime dateObject1 = DateTime.forInstant(date1.getTime(), TimeZone.getDefault());
DateTime dateObject2 = DateTime.forInstant(date2.getTime(), TimeZone.getDefault());
return dateObject1.isSameDayAs(dateObject2);
}
✅ 优点:API 简单,isSameDayAs
方法名直白。
❌ 缺点:社区活跃度远不如 Commons 或 Joda-Time,新项目引入需谨慎评估。
4. 总结
方法 | 推荐程度 | 适用场景 |
---|---|---|
LocalDate (Java 8+) |
✅✅✅ | 新项目首选,语义清晰 |
Instant.truncatedTo |
✅✅ | 需要基于 UTC 比较的场景 |
DateUtils.isSameDay |
✅✅✅ | 老项目快速修复,代码简洁 |
SimpleDateFormat |
⚠️ | 临时脚本,注意线程安全 |
Calendar |
❌ | 仅维护老代码 |
Joda-Time | ⚠️ | 已有项目,新项目不推荐 |
Date4J | ⚠️ | 小众库,按需选择 |
📌 最终建议:
- Java 8+ 项目:优先用
LocalDate
- 老项目想少写代码:上
Apache Commons Lang
- 别再手写
Calendar
比较了,太容易出错
完整示例代码已托管至 GitHub:https://github.com/baeldung/tutorials/tree/master/core-java-modules/core-java-date-operations-2