比较两个java.util.Date以查看它们是否在同一天


249

我需要比较两个Dates(例如date1date2),并得出在同一天boolean sameDay这两个Dates共享的true,否则返回false。

我怎样才能做到这一点?这里似乎有混乱的旋风……我想尽可能避免引入除JDK之外的其他依赖项。

需要说明的是:如果date1并且date2共享相同的年,月和日,sameDay则为true,否则为false。我意识到这需要了解时区...传入时区会很好,但是只要知道行为是什么,我就可以使用GMT或本地时间。

再次澄清:

date1 = 2008 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = true

date1 = 2009 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = false

date1 = 2008 Aug 03 12:00:00
date2 = 2008 Jun 03 12:00:00
  => sameDate = false

只是为了澄清一下-您想知道两个Date对象是否在一周的同一天?
Rob Heiser 2010年

您要比较完整日期(日,月,年)还是仅比较一个月日?
XpiritO

1
@Rob:不,同一天/每月/每年...我会澄清。
杰森S

那你为什么不使用“等于”呢?
XpiritO

7
如果小时/分钟/秒不同,则它们不相等。
杰森S

Answers:


415
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);
boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
                  cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);

请注意,“同一天”并不是一个简单的概念,而是涉及不同时区时听起来很简单。上面的代码将针对两个日期计算相对于其所运行的计算机所使用的时区的日期。如果这不是您所需要的,则在Calendar.getInstance()确定“同一天”的确切含义之后,您必须将相关的时区传递给呼叫。

是的,Joda Time LocalDate可以使整个过程变得更加整洁和轻松(尽管存在与时区相同的困难)。


谢谢,看起来它可以满足我的要求。就我而言,我正在比较系列中的连续日期,因此看起来我可以只使用Calendar实例而不是系列中的Date实例。
杰森S

1
@Jason这可能不是一个好主意。Calendar的主要问题在于,它是一个非常重要的类,具有很多内部状态,其中一些状态用在其equals()实现中。如果您不希望日期相等并且不将其放入HashMaps中,那应该很好。
Michael Borgwardt 2010年

很酷,谢谢,我只是使用这里要求的“比较当前和前一天”的逻辑。“ equals”和“ hashcode”函数永远都不会被调用。
杰森S

1
@UmerHayat:代码比较一年中的某天,而不是月份中的某天。只需进行较少的比较即可,代码更短。
Michael Borgwardt 2014年

2
建议:首先比较DAY_OF_YEAR,而不必检查年份。好吧,这并不是说比较int确实很昂贵,但是……
Martin P.

332

怎么样:

SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));

如果需要,还可以将时区设置为SimpleDateFormat。


2
(在我的情况下,无论如何我实际上都在使用SimpleDateFormat,所以似乎有点合适。)
Jason S 2010年

8
我真的很惊讶地看到这一点,但是即使从性能的角度来看,这种SimpleDateFormat方法实际上也比这里提到的使用Calendars的方法要快。平均而言,该Calendar方法只需花费一半的时间。(至少在我的系统上)。荣誉!
Michael Plautz 2014年

此答案的大部分成本是在SimpleDateFormat创建上。如果您想要更好的性能,请将其放在单例的threadLocal字段中
Thierry

1
我也在Swift中这样做。在大多数语言中,如此简单的事情需要同样的技巧,真是令人惊讶。C#是例外-if(d1.Date == d2.Date)...
alpsystems.com

2
这很丑。惊讶地看到这样的骇客遭到抨击。
Zsolt Safrany,

162

我使用“ apache commons lang”包来做到这一点(即org.apache.commons.lang.time.DateUtils

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects

2
这使用了外部依赖关系...但是对未来有所了解是一件好事。
杰森S

20
只需复制源代码并将其命名为一天即可:)
Anton Kuzmin 2012年

但是,如果任何一天为空,它将引发非法的参数异常,这在某些情况下并不理想。
raviraja19年

1
stackoverflow.com/a/2517824/1665809中所述,如果涉及不同的时区,则此解决方案可能不合适。
mrod

24

您可以通过计算每个日期的儒略日编号,然后进行比较来避免使用日历的外部依赖性和性能损失。

public static boolean isSameDay(Date date1, Date date2) {

    // Strip out the time part of each date.
    long julianDayNumber1 = date1.getTime() / MILLIS_PER_DAY;
    long julianDayNumber2 = date2.getTime() / MILLIS_PER_DAY;

    // If they now are equal then it is the same day.
    return julianDayNumber1 == julianDayNumber2;
}

4
但是请注意,由于夏令时,这没有考虑到一天的变化。但是,直线Date不封装时区的概念,因此没有办法非任意地解决这个问题。
2011年

3
顺便说一下,这是最快的解决方案-非常感谢,我一直在寻找什么;因为我要检查很多日期...
Ridcully 2012年

2
Nitpick:date1.getTime()不返回儒略日编号,而是自1970-01-01以来的毫秒数。差异很大。
Per Lindberg

2
这将在UTC时区中执行比较。如果您想要本地时区或地球上的其他地方,则无法知道得到的结果是否正确。
Ole VV

20

乔达时代

至于添加依赖项,恐怕java.util.Date和.Calendar确实很糟糕,以至于我对任何新项目所做的第一件事就是添加Joda-Time库。在Java 8中,您可以使用受Joda-Time启发的新java.time包。

Joda-Time的核心是DateTime课程。与java.util.Date不同,它了解已分配的时区(DateTimeZone)。从juDate转换时,分配一个区域。

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTimeQuébec = new DateTime( date , zone );

LocalDate

验证两个日期时间是否在同一日期上的一种方法是将其转换为LocalDate对象。

该转换取决于指定的时区。要比较LocalDate对象,必须已将它们转换为相同区域。

这是一个实用工具。

static public Boolean sameDate ( DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1 );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( dt1.getZone() ) );
    Boolean match = ld1.equals( ld2 );
    return match;
}

最好是另一个参数,指定时区,而不是假定应该使用第一个DateTime对象的时区。

static public Boolean sameDate ( DateTimeZone zone , DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1.withZone( zone ) );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( zone ) );
    return ld1.equals( ld2 );
}

字符串表示

另一种方法是创建每个日期时间的日期部分的字符串表示形式,然后比较字符串。

同样,分配的时区至关重要。

DateTimeFormatter formatter = ISODateTimeFormat.date();  // Static method.
String s1 = formatter.print( dateTime1 );
String s2 = formatter.print( dateTime2.withZone( dt1.getZone() )  );
Boolean match = s1.equals( s2 );
return match;

时间跨度

通用解决方案是定义时间跨度,然后询问该跨度是否包含您的目标。此示例代码在Joda-Time 2.4中。请注意,不建议使用与“午夜”相关的类。而是使用该withTimeAtStartOfDay方法。Joda-Time提供了三个类以各种方式表示时间跨度:“间隔”,“周期”和“持续时间”。

使用“半开放”方法,其中跨度的开始是包含端点的,结束是排除端点的。

目标的时区可以与间隔的时区不同。

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime target = new DateTime( 2012, 3, 4, 5, 6, 7, timeZone );
DateTime start = DateTime.now( timeZone ).withTimeAtStartOfDay();
DateTime stop = start.plusDays( 1 ).withTimeAtStartOfDay();
Interval interval = new Interval( start, stop );
boolean containsTarget = interval.contains( target );

java.time

Java 8和更高版本附带了java.time框架。受Joda-Time启发,由JSR 310定义,并由ThreeTen-Extra项目扩展。请参阅教程

Joda-Time的开发人员指示我们所有人在方便时都应立即转移到java.time。同时,Joda-Time继续作为一个积极维护的项目。但是希望以后的工作只会在java.time和ThreeTen-Extra中发生,而不是在Joda-Time中发生。

概括地说,总结java.time……这Instant是UTC时间轴上的时刻。应用时区(ZoneId)获取ZonedDateTime对象。动销的时间表,以获得的日期时间的模糊不定主意,使用“本地”类:LocalDateTimeLocalDateLocalTime

此答案的“乔达时间”部分中讨论的逻辑适用于java.time。

旧的java.util.Date类具有toInstant用于转换为java.time 的新方法。

Instant instant = yourJavaUtilDate.toInstant(); // Convert into java.time type.

确定日期需要一个时区。

ZoneId zoneId = ZoneId.of( "America/Montreal" );

我们将该时区对象应用于Instant以获得ZonedDateTime。从中,我们提取仅日期的值(a LocalDate),因为我们的目标是比较日期(而不是小时,分钟等)。

ZonedDateTime zdt1 = ZonedDateTime.ofInstant( instant , zoneId );
LocalDate localDate1 = LocalDate.from( zdt1 );

java.util.Date我们需要比较的第二个对象执行相同的操作。我将仅使用当前时刻。

ZonedDateTime zdt2 = ZonedDateTime.now( zoneId );
LocalDate localDate2 = LocalDate.from( zdt2 );

使用特殊isEqual方法测试相同的日期值。

Boolean sameDate = localDate1.isEqual( localDate2 );

java.time:或者您也可以做LocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
Cyber​​Mew '17

1
@Cyber​​Mew嗯?有没有fromDateFields()我的LocalDate
Ole VV

1
抱歉,我应该更清楚了。该注释与Joda-Time LocalDate类相关。
Cyber​​Mew '17

7

日期转换为Java 8 java.time.LocalDate 这里看到

LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

// compare dates
assertTrue("Not on the same day", localDate1.equals(localDate2));

这是我最喜欢的答案。如果你想控制使用,而不是依赖于计算机的设置时区,这是直接的投入,例如ZoneId.of("America/Phoenix")ZoneId.of("Europe/Budapest")替代系统默认值。
Ole VV

6

Java 8

如果您在项目中使用Java 8进行比较java.sql.Timestamp,则可以使用以下LocalDate类:

sameDate = date1.toLocalDateTime().toLocalDate().equals(date2.toLocalDateTime().toLocalDate());

如果使用java.util.Date,请查看不太模糊的Istvan答案。


使用Java 8是个好主意。你是否假设date1date2java.sql.Timestamp?根据他们的问题Date,我会明白的java.util.Date。同样,您的代码也使用计算机的时区设置,这在许多方面都可以。我仍然希望在代码中明确说明这一事实。
Ole VV

@ OleV.V。谢谢您的评论,我原来的回答确实是关于java.sql.Timestamp。如您所说,通常最好是显式设置时区。
amanteaux

5
private boolean isSameDay(Date date1, Date date2) {
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);
        boolean sameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
        boolean sameMonth = calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
        boolean sameDay = calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
        return (sameDay && sameMonth && sameYear);
    }

5

对于ANDROID用户:

您可以DateUtils.isToday(dateMilliseconds)用来检查给定的日期是否是当前日期。

API参考:https//developer.android.com/reference/android/text/format/DateUtils.html#isToday(long)


1
此方法使用自API 22起不推荐使用的对象Time:developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko

2
为Android开发人员+1。此方法使用原语long作为参数,只需使用DateUtils.isToday(x.getTime())(x是java.util.date实例)就可以了
jackycflau

1

除了Binil Thomas解决方案

public static boolean isOnSameDay(Timestamp... dates) {
    SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
    String date1 = fmt.format(dates[0]);
    for (Timestamp date : dates) {
        if (!fmt.format(date).equals(date1)) {
            return false;
        }
    }
    return true;
}

用法

    isOnSameDay(date1,date2,date3 ...);
//or 
    isOnSameDay(mydates);

1

对于Kotlin开发人员,这是具有比较格式化字符串方法的版本:

val sdf = SimpleDateFormat("yyMMdd")
if (sdf.format(date1) == sdf.format(date2)) {
    // same day
}

这不是最好的方法,但它又短又有效。


1
SimpleDateFormat类年前被取代由现代java.time.DateTimeFormatter在JSR 310定义的类表明其使用在2019年是不好的建议。
罗勒·布尔克

2
此代码忽略了时区的关键问题。在任何给定时刻,日期都会在全球范围内变化。
罗勒·布尔克

-4

您可以应用与SimpleDateFormat解决方案相同的逻辑,而无需依赖SimpleDateFormat

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()

6
这些方法在java.util.Date中已弃用。您应该使用日历来执行此操作。也是getYear,而不是getFullYear
Kris
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.