這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何理解Java Date Timestamp 日期比較的錯(cuò)誤分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
如東ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
場(chǎng)景重現(xiàn):
在MySQL數(shù)據(jù)庫(innodb engine)的tab表里有一個(gè)createAt字段,類型為datetime(6) 精確到毫秒。當(dāng)然大家知道m(xù)ysql的日期字段默認(rèn)只精確到秒級(jí)的,若要到毫秒微妙可定義為datetime(6), 從5.6.4版本開始支持
業(yè)務(wù)接口A通過ORM框架在表中存入一條記錄,這里createAt存入
2016-04-13 15:20:39.152
業(yè)務(wù)接口B需查到這條記錄載入實(shí)體類Entity中,其中createAt屬性為Date類型,值即為上方的日期。 (注:添加了@Temporal(TemporalType.TIMESTAMP)注解,則該字段類型為Timestamp。本場(chǎng)景是Date)
獲取當(dāng)前的系統(tǒng)時(shí)間, 注意這里用了Timestamp
Date now = new Timestamp(System.currentTimeMillis())
毫秒格式化字符串為 2016-04-13 15:20:39.952
你會(huì)發(fā)現(xiàn)多了后面的毫秒小數(shù) 952
調(diào)用 now.after(entire.createAt) 期望返回true, 但實(shí)際返回false??!
這樣就出現(xiàn)了一個(gè)怪異的情形:本來先存入數(shù)據(jù)庫一條帶有時(shí)間戳的記錄,取出來后跟當(dāng)前的系統(tǒng)時(shí)間戳進(jìn)行對(duì)比,竟然發(fā)現(xiàn)之前存入的時(shí)間是在未來?!
這里到底發(fā)生了什么導(dǎo)致
當(dāng)前系統(tǒng)時(shí)間 .after( 之前存入的時(shí)間 ) = false?
源碼分析
通過查看源碼可以看到問題所在。
首先,Date類方法getMillisOf()的第一個(gè)if判斷中date不為空,但是isNormalized()返回true。這個(gè)normalize是CalendarDate中的私有屬性,只要調(diào)用過set/add之類修改時(shí)間的方法就會(huì)變成false。其他說明請(qǐng)看cdate的注釋。
其次,Date構(gòu)造方法直接將系統(tǒng)的毫秒級(jí)別的long數(shù)值賦給了fasttime。
再來,Timestamp構(gòu)造方法截取秒級(jí)的時(shí)間存入fasttime, 將毫秒微妙計(jì)入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)試分解
然后我們對(duì)比下new Date() vs new Timestamp(System.currentTimeMillis())
兩者存儲(chǔ)方式的區(qū)別通過下面兩個(gè)圖就可以清楚分辨,只要注意fastTime。
Date fastTime的最后三位是956,說明是精確到毫秒的
Timestamp的最后三位是000,說明被截取到秒,而真正的毫秒166被放到nanos中了
結(jié)論說明
錯(cuò)誤的根源是混用了Date 和 Timestamp, 導(dǎo)致日期比對(duì)失效。
ORM從數(shù)據(jù)庫中取出的時(shí)間類型是Date first(見文末圖),而當(dāng)前的時(shí)間戳獲取方式錯(cuò)用了Timestamp second(見文末圖), 只要修改為new Date() 就可以了。
如果無法避免混用,那就不要使用after() before()做日期對(duì)比!
直接用 getTime() 比較long的大小即可!有興趣的同學(xué)可以看下Timestamp getTime()的源碼, 它會(huì)把nanos拼裝回?cái)?shù)值中!
如何重現(xiàn)
兩個(gè)long類型的數(shù)據(jù),一個(gè)800毫秒,一個(gè)900毫秒,可以看出after(before類似, compareTo慎用)返回的結(jié)果是錯(cuò)誤的。
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, 錯(cuò)誤結(jié)果 System.out.println(t.compareTo(d)); //1, 正確結(jié)果...Timestamp的compareTo方法被重載了所以這里沒問題。 System.out.println(d.compareTo(t)); //1, 錯(cuò)誤結(jié)果...Date的compareTo方法還是錯(cuò)誤的。 System.out.println(t.getTime() > d.getTime()); //true, 正確結(jié)果
}
上述就是小編為大家分享的如何理解Java Date Timestamp 日期比較的錯(cuò)誤分析了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)站題目:如何理解JavaDateTimestamp日期比較的錯(cuò)誤分析
鏈接分享:http://jinyejixie.com/article36/ggeopg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、動(dòng)態(tài)網(wǎng)站、網(wǎng)站設(shè)計(jì)公司、建站公司、網(wǎng)頁設(shè)計(jì)公司、網(wǎng)站營(yíng)銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)