Java Date#compareTo()中遇到的坑

今天遇到一个日期比较的bug,打断点跟进代码一看之后发现,在比较两个完全一样的日期时,尽然判断不相等。仔细看了下这2个日期后,发现他们一个是正常的date,另一个虽然也是一个date,但是是指向的timestamp。像下面这段代码这样:

1
2
3
4
5
6
7
8
9
@Test
public void dateCompareTest01() throws ParseException {
    String DateStr = "2020-07-03 23:59:59.999";
    Timestamp ts = Timestamp.valueOf(DateStr);
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    Date date1 = format.parse(DateStr);
    Date date2 = ts;
    System.out.println(date1.compareTo(date2));
}

我们看看这两个日期长啥样:
在这里插入图片描述
可以看到这两个日期的格式不太一样,其根本原因在于一个是正常的date,一个是指向timestamp的date,而date与timestamp的结构又不一样导致的。
在date中,fastTime和cdate都包含有毫秒,而timestamp里,毫秒被拆了出来,用nanos来存,所以fastTime和cdate里都不包含毫秒。

解决方法:调用getTime()去比较。因为timestamp的getTime()会把毫秒拼接上来

我们再来了解下Date的compareTo()方法:

1
2
3
4
5
public int compareTo(Date anotherDate) {
    long thisTime = getMillisOf(this);
    long anotherTime = getMillisOf(anotherDate);
    return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}

getMillisOf()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static final long getMillisOf(Date date) {
    /*
    * 由cdate字段的注解可知:
    *   1.如果cdate为null时,fastTime精确到毫秒。
    *   2.如果isNormalized()为true时,cdate和fastTime同步、保持一致,否则fastTime不生效,用cdate表示时间。
    * 所以当cdate为null或者isNormalized()为true时,直接取fastTime就好。
    * 关于normalized字段,默认是true,如果有set/add操作的话,就会改为false。
    * */
    if (date.cdate == null || date.cdate.isNormalized()) {
        return date.fastTime;
    }
    BaseCalendar.Date d = (BaseCalendar.Date) date.cdate.clone();
    return gcal.getTime(d);
}

date1和date2在比较时,会根据cdateisNormalized()的值来判断用fastTime还是用cdate来比较。但是不管用哪个,比较时都会判断不相等。