如何在Java中比较日期?

447

如何在Java中比较日期间的大小?

示例:

date122-02-2010
date2是今天07-04-2010
date325-12-2010

其中date3始终大于date1date2始终为今天。如何验证今天的日期是否在date1date3之间?

11个回答

685

Date 类有 beforeafter 方法,可以通过 比较 方法进行比较:

if(todayDate.after(historyDate) && todayDate.before(futureDate)) {
    // In between
}

对于包含比较:

if(!historyDate.after(todayDate) && !futureDate.before(todayDate)) {
    /* historyDate <= todayDate <= futureDate */ 
}
您也可以尝试使用Joda-Time,但请注意以下几点:

Joda-Time是Java SE 8之前的事实标准日期和时间库。现在要求用户迁移到java.time(JSR-310)。

Java 6和7以及Android都可用后移植版。

8
这个边界是包容的还是排外的? - Daniel Hári
1
@DanielHári 不是包含的。你可以使用第一条评论中建议的解决方案,或者使用CompareTo()>=0。 - Mifmif
通常情况下,“左闭右开”是默认的,这也是为什么我认为应该明确指定。使用“左闭右开”的边界,您可以轻松地指定例如一个月的时间间隔:[2016/04/01,2016/05/01],这也是为什么在许多用例中将其作为默认值使用的原因。 - Daniel Hári
1
FYI,诸如java.util.Date的老旧日期时间类现已成为遗留系统,被 Java 8 和 Java 9 内置的 java.time 类所取代。请参阅 Oracle 的教程 - Basil Bourque
@BasilBourque 是的,正确,因此提到了 Joda 和 java.time 适用于 Java 8 的注释,这是现在的标准版本(与我写这个答案的时候相反)。当然,随时欢迎编辑此答案并添加更多最新的示例代码! ;) (编辑:啊,我看到你自己已经提供了带有这些示例的答案) - Bart Kiers

147

与日期、字节、长整型、整型完美配合。 - Giancarlo Ventura Granados
1
有时候它不起作用。我在主方法中多次触发以检查相等性。大多数情况下它会说相等,但并非所有时候都是这样。 - Syed
@Syed,你能分享一下你的代码吗? - Chandra Sekar
@ChandraSekarS,请尝试使用相同的日期作为主方法代码并查看输出。有时会返回0,有时会返回1。 - Syed

131
以下是比较日期的最常见方法(我偏好方法1): 方法1:使用Date.before()Date.after()Date.equals()
if (date1.after(date2)) {
    System.out.println("Date1 is after Date2");
}

if (date1.before(date2)) {
    System.out.println("Date1 is before Date2");
}

if (date1.equals(date2)) {
    System.out.println("Date1 is equal Date2");
}

方法二:Date.compareTo()

if (date1.compareTo(date2) > 0) {
    System.out.println("Date1 is after Date2");
} else if (date1.compareTo(date2) < 0) {
    System.out.println("Date1 is before Date2");
} else {
    System.out.println("Date1 is equal to Date2");
}

方法三: Calendar.before()Calendar.after()Calendar.equals()

Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);

if (cal1.after(cal2)) {
    System.out.println("Date1 is after Date2");
}

if (cal1.before(cal2)) {
    System.out.println("Date1 is before Date2");
}

if (cal1.equals(cal2)) {
    System.out.println("Date1 is equal Date2");
}

请注意,像java.util.Datejava.util.Calendarjava.text.SimpleDateFormat这样的老旧日期时间类现在已经成为遗留系统,被内置于Java 8和Java 9中的java.time类所取代。请参阅Oracle的教程 - Basil Bourque
compareTo方法可能会出现零星问题。最好使用另一种方式。 - Sapthaka

43

简而言之

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;
Boolean isBetween = 
    ( ! today.isBefore( localDate1 ) )  // “not-before” is short for “is-equal-to or later-than”.
    &&
    today.isBefore( localDate3 ) ; 

或者更好的方法是将ThreeTen-Extra库添加到您的项目中。

LocalDateRange.of(
    LocalDate.of( … ) ,
    LocalDate.of( … )
).contains(
    LocalDate.now()
)

半开区间方法,起始点包含,而结束点不包含

格式选择不佳

顺便说一句,那是一个文本表示日期或日期时间值的不好的格式选择。尽可能地,坚持使用标准ISO 8601格式。ISO 8601格式是明确无歧义、跨越人类文化理解范围、易于机器解析的。

对于仅包含日期的值,标准格式为YYYY-MM-DD。请注意,这种格式在按字母顺序排序时具有年代顺序。

LocalDate

LocalDate类表示仅包含日期、不包含时间和时区的值。

时区对于确定日期非常重要。对于任何给定的时刻,由于时区不同,世界各地的日期都会有所不同。例如,在法国巴黎的午夜过后几分钟就是新的一天,而在加拿大魁北克省蒙特利尔仍然是“昨天”。

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

Table of all date-time types in Java, both modern and legacy

DateTimeFormatter

由于您的输入字符串不是标准格式,我们必须定义一个格式化模式来匹配。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu" );

使用它来解析输入字符串。
LocalDate start = LocalDate.parse( "22-02-2010" , f );
LocalDate stop = LocalDate.parse( "25-12-2010" , f );

在日期时间操作中,通常最好使用半开区间的方法来定义时间跨度,其中开始时间是包含的,而结束时间是不包含的。因此,我们想知道今天是否与开始时间相同或更晚,并且在结束时间之前。更简洁地说,“与开始时间相同或更晚”可以表示为“不早于开始时间”。
Boolean intervalContainsToday = ( ! today.isBefore( start ) ) && today.isBefore( stop ) ;

请查看gstackoverflow的回答,其中列出了您可以调用的比较方法列表。


关于 java.time

java.time 框架是内置于 Java 8 及更高版本中的。这些类替代了老旧且麻烦的 legacy 日期时间类,如 java.util.DateCalendarSimpleDateFormat

想要了解更多,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需字符串,无需java.sql.*类。Hibernate 5和JPA 2.2支持java.time

在哪里获取java.time类?

ThreeTen-Extra项目扩展了java.time的附加类。该项目是java.time可能未来添加内容的试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter,以及more

更新:下面的“Joda-Time”部分保持不变,作为历史记录。现在处于维护模式Joda-Time项目建议迁移到java.time类。

Joda-Time

其他答案对于捆绑的java.util.Date和java.util.Calendar类是正确的。但是这些类通常会出现问题。因此,以下是使用Joda-Time 2.3库的示例代码。

如果您真正想要一个没有时间部分和时区的日期,那么请使用Joda-Time中的LocalDate类。该类提供了比较方法,包括compareTo(与Java Comparators一起使用)、isBeforeisAfterisEqual
输入...
String string1 = "22-02-2010";
String string2 = "07-04-2010";
String string3 = "25-12-2010";

定义一个格式化程序,描述输入字符串...
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd-MM-yyyy" );

使用格式化程序将字符串解析为LocalDate对象...
LocalDate localDate1 = formatter.parseLocalDate( string1 );
LocalDate localDate2 = formatter.parseLocalDate( string2 );
LocalDate localDate3 = formatter.parseLocalDate( string3 );

boolean is1After2 = localDate1.isAfter( localDate2 );
boolean is2Before3 = localDate2.isBefore( localDate3 );

转储到控制台...

System.out.println( "Dates: " + localDate1 + " " + localDate2 + " " + localDate3 );
System.out.println( "is1After2 " + is1After2 );
System.out.println( "is2Before3 " + is2Before3 );

当运行时...
Dates: 2010-02-22 2010-04-07 2010-12-25
is1After2 false
is2Before3 true

请确认第二个值是否在另外两个值之间(排除两个端点,意味着不等于任何一个端点)...

boolean is2Between1And3 = ( ( localDate2.isAfter( localDate1 ) ) && ( localDate2.isBefore( localDate3 ) ) );

处理时间段

如果你需要处理时间段,我建议使用Joda-Time中的DurationIntervalPeriod类。这些类提供了诸如overlapcontains等方法,使比较变得容易。

对于文本表示,可以查看ISO 8601标准的:

  • duration
    格式: PnYnMnDTnHnMnS
    示例: P3Y6M4DT12H30M5S
    (意为“三年六个月四天十二小时三十分钟和五秒”)
  • interval
    格式: 开始时间/结束时间
    示例: 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z

Joda-Time类可以处理这两种格式的字符串,既可以作为输入(解析),也可以作为输出(生成字符串)。

Joda-Time使用半开区间方法进行比较,其中时间段的开始部分是包含的,而结束部分是不包含的。这种方法对于处理时间段非常明智。请在StackOverflow上搜索更多信息。


提到“(右)半开区间”方法是判断“是否在时间跨度内”的一个极好的实践技巧。 - hc_dev

23

比较这两个日期:

  Date today = new Date();                   
  Date myDate = new Date(today.getYear(),today.getMonth()-1,today.getDay());
  System.out.println("My Date is"+myDate);    
  System.out.println("Today Date is"+today);
  if (today.compareTo(myDate)<0)
      System.out.println("Today Date is Lesser than my Date");
  else if (today.compareTo(myDate)>0)
      System.out.println("Today Date is Greater than my date"); 
  else
      System.out.println("Both Dates are equal"); 

6
我认为 "new Date(today.getYear(),today.getMonth()-1,today.getDay());" 已经作废了。http://download.oracle.com/javase/6/docs/api/java/util/Date.html - Dr. No
@Muath:虽然我不是100%确定,但我认为这是因为Date中的月份组件是从零开始索引的。 - Lii
我认为那里的else部分没有起作用 :) - Sachith Wickramaarachchi

15

如何下载这三个类? - Punithapriya
1
@Punithapriya,这些类存在于Java 8标准库中。因此只需下载Java 8即可。 - gstackoverflow
是的,我已经看到了,谢谢。 - Punithapriya

11
你可以使用 Date.getTime() 方法,它会返回一个表示该日期距离格林威治标准时间1970年1月1日午夜零点的毫秒数。
这意味着你可以像比较数字一样比较它们。
if (date1.getTime() <= date.getTime() && date.getTime() <= date2.getTime()) {
    /*
     * date is between date1 and date2 (both inclusive)
     */
}

/*
 * when date1 = 2015-01-01 and date2 = 2015-01-10 then
 * returns true for:
 * 2015-01-01
 * 2015-01-01 00:00:01
 * 2015-01-02
 * 2015-01-10
 * returns false for:
 * 2014-12-31 23:59:59
 * 2015-01-10 00:00:01
 * 
 * if one or both dates are exclusive then change <= to <
 */

8

试试这个

public static boolean compareDates(String psDate1, String psDate2) throws ParseException{
        SimpleDateFormat dateFormat = new SimpleDateFormat ("dd/MM/yyyy");
        Date date1 = dateFormat.parse(psDate1);
        Date date2 = dateFormat.parse(psDate2);
        if(date2.after(date1)) {
            return true;
        } else {
            return false;
        }
    }

5
使用 getTime() 方法获取日期的数字值,然后使用返回的值进行比较。

0

这段代码根据韩国地区设置,确定今天是否在某个时间段内。

    Calendar cstart = Calendar.getInstance(Locale.KOREA);
    cstart.clear();
    cstart.set(startyear, startmonth, startday);


    Calendar cend = Calendar.getInstance(Locale.KOREA);
    cend.clear();
    cend.set(endyear, endmonth, endday);

    Calendar c = Calendar.getInstance(Locale.KOREA);

    if(c.after(cstart) && c.before(cend)) {
        // today is in startyear/startmonth/startday ~ endyear/endmonth/endday
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接