【问题标题】:Calendar dates are not equal日历日期不相等
【发布时间】:2020-07-29 16:06:13
【问题描述】:

我在使用 java 日历日期时遇到问题。 基本上我有一个假期列表,想检查一个日期是否属于 到列表中。仍然对 Calendar 类的方式感到困惑 正在创建和格式化日期。

请看以下代码示例:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class CompareCalendarDates {
public static void main(String args[]) {
    Date date1 = null;
    try {
        date1=  new SimpleDateFormat("yyyy-MM-dd").parse("2019-12-25");
    } catch (ParseException e) {
        date1 = null;
    }

    Calendar calendar1 = Calendar.getInstance();
    calendar1.clear();
    calendar1.setTime(date1);

    Calendar calendar2 = Calendar.getInstance();
    calendar2.clear();
    calendar2.setLenient(false); // Don't automatically convert invalid date.
    calendar2.set(2019, 11, 25, 0, 0, 0);
    calendar2.getTimeInMillis();

    boolean isEqual = calendar1.equals(calendar2);
    System.out.println ("Are to dates equal: " + isEqual);
}
}

我正在使用日历类中的不同方法创建 2 个日期。 在我看来,日期应该相等,但事实并非如此。 我错过了什么?方法之间有什么区别 setTime() 和设置?

【问题讨论】:

  • 不要使用Calendar,而是使用Java 8中引入的java.time类型。

标签: java date calendar compare


【解决方案1】:

你应该使用

boolean isEqual = calendar1.getTime().equals(calendar2.getTime());

代替

 boolean isEqual = calendar1.equals(calendar2);

因为 Calendar 类中的 equals() 方法如下所示:

public boolean equals(Object obj) {
    return obj instanceof Date && getTime() == ((Date) obj).getTime();
}

因此它会检查这两个日历中的日期是否是相同对象,在您的情况下,它们不是相同的对象,它们只是具有相同的值。

【讨论】:

    【解决方案2】:

    tl;博士

    LocalDate
    .parse( "2019-12-25" ) 
    .isEqual(
        LocalDate.of( 2019 , Month.DECEMBER , 25 )
    )
    

    是的

    详情

    永远不要使用Date/Calendar。仅使用 java.time 类。

    LocalDate::isEqual

    对于没有时间和时区的仅日期值,请使用LocalDate

    LocalDate ld = LocalDate.parse( "2019-12-25" ) ;
    
    LocalDate other = LocalDate.of( 2019, 11, 25 ) ;
    

    使用isEqualisBeforeisAfter进行比较。

    boolean sameDates = ld.isEqual( other ) ;
    

    这个问题已经在 Stack Overflow 上解决过很多次了。所以我保持这个答案简短。搜索以了解更多信息。

    12 月与 11 月

    与那些遗留类不同,java.time 类使用合理的编号。 1 月至 12 月的月份数为 1-12。

    因此请注意,您的一对输入不同,一个用于 12 月,一个用于 11 月。

    为清楚起见,您可以使用Month 枚举而不是整数。枚举的另一个好处是确保值有效。

    LocalDate other = LocalDate.of( 2019 , Month.NOVEMBER , 25 ) ;
    

    【讨论】:

    • 我必须在我的遗留代码中使用日历,但我会牢记您的建议,以便将来进行代码重构。
    • @MagdelenaFairfax 您可以通过调用添加到旧类的新from…to… 转换方法轻松地来回转换。
    【解决方案3】:

    java.time

    如果您从旧 API 获得了一个 Calendar 对象,而您现在无法升级到现代 Java 日期和时间 API java.time,并且您想知道它是否表示与一些字符串,将两者都转换为LocalDate 并使用isEqual 方法进行比较:

        String date1Str = "2019-12-25";
        Calendar calendar2 = new GregorianCalendar(2019, Calendar.DECEMBER, 25);
        
        LocalDate date1 = LocalDate.parse(date1Str);
        LocalDate date2 = ((GregorianCalendar) calendar2).toZonedDateTime().toLocalDate();
        
        boolean isEqual = date1.isEqual(date2);
        System.out.println ("Are to dates equal: " + isEqual);
    

    输出:

    到日期是否相等:true

    LocalDate 是一个没有时间、时区以及老式Calendar 对象所携带的所有其他内容的日期。所以比较两个LocalDate 会得到你所期望的结果。

    你的代码出了什么问题?

    虽然您的两个 Calendar 对象确实表示相同的时间点和相同的日历日期(这不是同一件事),但还是有一些区别,例如:

    1. calendar1 设置了所有字段,而 calendar2 设置了一些未计算的字段,例如时代、一年中的星期、上午/下午、毫秒和区域偏移。
    2. calendar1 宽容,calendar2 不宽容。

    我相信这足以使对象不被视为相等。我还没有检查文档的确切标准。出于好奇,您可能会这样做,但我建议您不需要这样做,因为您不需要比较两个 Calendar 对象是否相等。

    为了回答您的问题,setTime() 似乎设置了所有字段,而 set 显然只设置了一些字段。我惊讶地发现getTimeInMillis() 没有计算其余部分,但它没有。

    链接

    Oracle tutorial: Date Time 解释如何使用 java.time。

    【讨论】:

      猜你喜欢
      • 2019-05-05
      • 2018-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-06
      • 1970-01-01
      相关资源
      最近更新 更多