Java Date#compareTo()中遇到的坑
今天遇到一个日期比较的bug,打断点跟进代码一看之后发现,在比较两个完全一样的日期时,尽然判断不相等。仔细看了下这2个日期后,发现他们一个是正常的date,另一个虽然也是一个date,但是是指向的timestamp。像下面这段代码这样:
@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()方法:
public int compareTo(Date anotherDate) {
long thisTime = getMillisOf(this);
long anotherTime = getMillisOf(anotherDate);
return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}
getMillisOf()方法:
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在比较时,会根据cdate和isNormalized()的值来判断用fastTime还是用cdate来比较。但是不管用哪个,比较时都会判断不相等。
