您好,登錄后才能下訂單哦!
這期內(nèi)容當中小編將會給大家?guī)碛嘘P(guān)如何理解Java Date Timestamp 日期比較的錯誤分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
場景重現(xiàn):
在mysql數(shù)據(jù)庫(innodb engine)的tab表里有一個createAt字段,類型為datetime(6) 精確到毫秒。當然大家知道m(xù)ysql的日期字段默認只精確到秒級的,若要到毫秒微妙可定義為datetime(6), 從5.6.4版本開始支持
業(yè)務(wù)接口A通過ORM框架在表中存入一條記錄,這里createAt存入
2016-04-13 15:20:39.152
業(yè)務(wù)接口B需查到這條記錄載入實體類Entity中,其中createAt屬性為Date類型,值即為上方的日期。 (注:添加了@Temporal(TemporalType.TIMESTAMP)注解,則該字段類型為Timestamp。本場景是Date)
獲取當前的系統(tǒng)時間, 注意這里用了Timestamp
Date now = new Timestamp(System.currentTimeMillis())
毫秒格式化字符串為 2016-04-13 15:20:39.952
你會發(fā)現(xiàn)多了后面的毫秒小數(shù) 952
調(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é)論說明
錯誤的根源是混用了Date 和 Timestamp, 導(dǎo)致日期比對失效。
ORM從數(shù)據(jù)庫中取出的時間類型是Date first(見文末圖),而當前的時間戳獲取方式錯用了Timestamp second(見文末圖), 只要修改為new Date() 就可以了。
如果無法避免混用,那就不要使用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è)資訊頻道。
免責聲明:本站發(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)容。