溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Java Date Timestamp 日期比較的錯誤分析

發(fā)布時間:2021-11-20 14:12:21 來源:億速云 閱讀:182 作者:柒染 欄目:大數(shù)據(jù)

這期內(nèi)容當中小編將會給大家?guī)碛嘘P(guān)如何理解Java Date Timestamp 日期比較的錯誤分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

場景重現(xiàn):

  1. mysql數(shù)據(jù)庫(innodb engine)的tab表里有一個createAt字段,類型為datetime(6) 精確到毫秒。當然大家知道m(xù)ysql的日期字段默認只精確到秒級的,若要到毫秒微妙可定義為datetime(6), 從5.6.4版本開始支持

  2. 業(yè)務(wù)接口A通過ORM框架在表中存入一條記錄,這里createAt存入
    2016-04-13 15:20:39.152

  3. 業(yè)務(wù)接口B需查到這條記錄載入實體類Entity中,其中createAt屬性為Date類型,值即為上方的日期。 (注:添加了@Temporal(TemporalType.TIMESTAMP)注解,則該字段類型為Timestamp。本場景是Date)

  4. 獲取當前的系統(tǒng)時間, 注意這里用了Timestamp
    Date now = new Timestamp(System.currentTimeMillis())
    毫秒格式化字符串為 2016-04-13 15:20:39.952
    你會發(fā)現(xiàn)多了后面的毫秒小數(shù) 952

  5. 調(diào)用 now.after(entire.createAt) 期望返回true, 但實際返回false??!


這樣就出現(xiàn)了一個怪異的情形:本來先存入數(shù)據(jù)庫一條帶有時間戳的記錄,取出來后跟當前的系統(tǒng)時間戳進行對比,竟然發(fā)現(xiàn)之前存入的時間是在未來?!

這里到底發(fā)生了什么導(dǎo)致
當前系統(tǒng)時間 .after( 之前存入的時間 ) = false?

源碼分析

通過查看源碼可以看到問題所在。
首先,Date類方法getMillisOf()的第一個if判斷中date不為空,但是isNormalized()返回true。這個normalize是CalendarDate中的私有屬性,只要調(diào)用過set/add之類修改時間的方法就會變成false。其他說明請看cdate的注釋。
其次,Date構(gòu)造方法直接將系統(tǒng)的毫秒級別的long數(shù)值賦給了fasttime。
再來,Timestamp構(gòu)造方法截取秒級的時間存入fasttime, 將毫秒微妙計入nanos中。

/*
 * If cdate is null, then fastTime indicates the time in millis.
 * If cdate.isNormalized() is true, then fastTime and cdate are in
 * synch. Otherwise, fastTime is ignored, and cdate indicates the
 * time.
 */

private transient BaseCalendar.Date cdate;public Date() {    
   this(System.currentTimeMillis()); }

public Date(long date) {    fastTime = date; }

static final long getMillisOf(Date date) {    
   if (date.cdate == null || date.cdate.isNormalized()) {        
       return date.fastTime;    }    BaseCalendar.Date d = (BaseCalendar.Date) date.cdate.clone();    
   return gcal.getTime(d); } Timestamp構(gòu)造方法:
   public Timestamp(long time) {    
   super((time/1000)*1000); //毫秒除1000取整只剩下秒了,再乘以1000作為毫秒數(shù)值    nanos = (int)((time%1000) * 1000000);    
   if (nanos < 0) {        nanos = 1000000000 + nanos;        
       super.setTime(((time/1000)-1)*1000);    } }
 


調(diào)試分解

然后我們對比下new Date() vs new Timestamp(System.currentTimeMillis())
兩者存儲方式的區(qū)別通過下面兩個圖就可以清楚分辨,只要注意fastTime。

  • Date fastTime的最后三位是956,說明是精確到毫秒的

  • Timestamp的最后三位是000,說明被截取到秒,而真正的毫秒166被放到nanos中了


結(jié)論說明

  1. 錯誤的根源是混用了Date 和 Timestamp, 導(dǎo)致日期比對失效。
    ORM從數(shù)據(jù)庫中取出的時間類型是Date first(見文末圖),而當前的時間戳獲取方式錯用了Timestamp second(見文末圖), 只要修改為new Date() 就可以了。

  2. 如果無法避免混用,那就不要使用after() before()做日期對比!
    直接用 getTime() 比較long的大小即可!有興趣的同學(xué)可以看下Timestamp getTime()的源碼, 它會把nanos拼裝回數(shù)值中!


如何重現(xiàn)

兩個long類型的數(shù)據(jù),一個800毫秒,一個900毫秒,可以看出after(before類似, compareTo慎用)返回的結(jié)果是錯誤的。


public static void main(String[] args) throws IOException {
    Date d = new Date(1473247063800L);
    Date t = new Timestamp(1473247063900L);
    System.out.println(d.getTime());
    System.out.println(t.getTime());
    System.out.println(t.after(d));  //false, 錯誤結(jié)果
    System.out.println(t.compareTo(d));  //1, 正確結(jié)果...Timestamp的compareTo方法被重載了所以這里沒問題。
    System.out.println(d.compareTo(t));  //1, 錯誤結(jié)果...Date的compareTo方法還是錯誤的。
    System.out.println(t.getTime() > d.getTime()); //true, 正確結(jié)果
}

上述就是小編為大家分享的如何理解Java Date Timestamp 日期比較的錯誤分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI