您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何理解Java Date Timestamp 日期比較的錯誤分析,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
場景重現:
在mysql數據庫(innodb engine)的tab表里有一個createAt字段,類型為datetime(6) 精確到毫秒。當然大家知道mysql的日期字段默認只精確到秒級的,若要到毫秒微妙可定義為datetime(6), 從5.6.4版本開始支持
業務接口A通過ORM框架在表中存入一條記錄,這里createAt存入
2016-04-13 15:20:39.152
業務接口B需查到這條記錄載入實體類Entity中,其中createAt屬性為Date類型,值即為上方的日期。 (注:添加了@Temporal(TemporalType.TIMESTAMP)注解,則該字段類型為Timestamp。本場景是Date)
獲取當前的系統時間, 注意這里用了Timestamp
Date now = new Timestamp(System.currentTimeMillis())
毫秒格式化字符串為 2016-04-13 15:20:39.952
你會發現多了后面的毫秒小數 952
調用 now.after(entire.createAt) 期望返回true, 但實際返回false!!
這樣就出現了一個怪異的情形:本來先存入數據庫一條帶有時間戳的記錄,取出來后跟當前的系統時間戳進行對比,竟然發現之前存入的時間是在未來?!
這里到底發生了什么導致
當前系統時間 .after( 之前存入的時間 ) = false?
源碼分析
通過查看源碼可以看到問題所在。
首先,Date類方法getMillisOf()的第一個if判斷中date不為空,但是isNormalized()返回true。這個normalize是CalendarDate中的私有屬性,只要調用過set/add之類修改時間的方法就會變成false。其他說明請看cdate的注釋。
其次,Date構造方法直接將系統的毫秒級別的long數值賦給了fasttime。
再來,Timestamp構造方法截取秒級的時間存入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構造方法:
public Timestamp(long time) {
super((time/1000)*1000); //毫秒除1000取整只剩下秒了,再乘以1000作為毫秒數值 nanos = (int)((time%1000) * 1000000);
if (nanos < 0) { nanos = 1000000000 + nanos;
super.setTime(((time/1000)-1)*1000); } }
調試分解
然后我們對比下new Date() vs new Timestamp(System.currentTimeMillis())
兩者存儲方式的區別通過下面兩個圖就可以清楚分辨,只要注意fastTime。
Date fastTime的最后三位是956,說明是精確到毫秒的
Timestamp的最后三位是000,說明被截取到秒,而真正的毫秒166被放到nanos中了
結論說明
錯誤的根源是混用了Date 和 Timestamp, 導致日期比對失效。
ORM從數據庫中取出的時間類型是Date first(見文末圖),而當前的時間戳獲取方式錯用了Timestamp second(見文末圖), 只要修改為new Date() 就可以了。
如果無法避免混用,那就不要使用after() before()做日期對比!
直接用 getTime() 比較long的大小即可!有興趣的同學可以看下Timestamp getTime()的源碼, 它會把nanos拼裝回數值中!
如何重現
兩個long類型的數據,一個800毫秒,一個900毫秒,可以看出after(before類似, compareTo慎用)返回的結果是錯誤的。
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, 錯誤結果 System.out.println(t.compareTo(d)); //1, 正確結果...Timestamp的compareTo方法被重載了所以這里沒問題。 System.out.println(d.compareTo(t)); //1, 錯誤結果...Date的compareTo方法還是錯誤的。 System.out.println(t.getTime() > d.getTime()); //true, 正確結果
}
上述就是小編為大家分享的如何理解Java Date Timestamp 日期比較的錯誤分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。