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


原始标题:Checking if Two Java Dates Are on the Same Day