你可能不會注意的Timestamp

提起java裏面的時間戳,相信很多人都用過。
不就是java.sql.Timestamp類,兩個構造器,13個方法,這也許屬於java中最簡單的基礎類了。

俗話說淹死的都是會游泳的,同樣在開發中讓我們栽跟頭的往往都是耳熟能詳的一些類庫。

引子

首先讓我們看看以下幾行代碼的輸出結果吧


Timestamp t1 = new Timestamp(0);
System.out.println(t1);
Timestamp t2 = Timestamp.valueOf("2037-12-31 23:59:59");
System.out.println(t2.getTime());
System.out.println(t2.getTime() == new Long(Integer.MAX_VALUE) * 1000);
Timestamp t3 = new Timestamp(new Long(Integer.MAX_VALUE) * 1000);
System.out.println(t3);Timestamp t21 = Timestamp.valueOf("9999-12-31 23:59:59");
System.out.println(t21.getTime());
Timestamp t22 = new Timestamp(t21.getTime());
System.out.println(t22);Date d1 = new Date(0);
System.out.println(d1.equals(t1));
System.out.println(t1.equals(d1));

如果你能很明確的說出每一個輸出結果是什麼以及爲什麼,那麼你已經不需要繼續往下看了。
如果你對一個或者更多的結果感到迷茫,就讓我們來一起踩踩坑吧。

時間範圍

要搞清楚Timestamp類的範圍方法有很多,可以看文檔、看源代碼,寫個代碼測試一下或者搜索引擎看看別人怎麼說。
當我們實踐了上面的一種或者多種方法後我們再回過頭來看看我們引子裏的第一段和第二段代碼的輸出結果。


Timestamp t1 = new Timestamp(0);
System.out.println(t1);//1970-01-01 08:00:00.0

有沒有看着很熟悉?跟格林尼治標準時(計算機用的比較多的說法是UTC)就差8個小時,那也很好理解我們是東八區嘛。
換句話說Timestamp類的開始時間可以認爲是格林尼治標準時,在不同時區使用會加上時區的偏移量。


Timestamp t2 = Timestamp.valueOf("2037-12-31 23:59:59");
System.out.println(t2.getTime());//2145887999000
System.out.println(t2.getTime() == new Long(Integer.MAX_VALUE) * 1000);//false
Timestamp t3 = new Timestamp(new Long(Integer.MAX_VALUE) * 1000);
System.out.println(t3);//2038-01-19 11:14:07.0
Timestamp t21 = Timestamp.valueOf("9999-12-31 23:59:59");
System.out.println(t21.getTime());//253402271999000
Timestamp t22 = new Timestamp(t21.getTime());
System.out.println(t22);//9999-12-31 23:59:59.0

上面幾段代碼其實都是根據網上關於Timestamp的最大值做出的驗證

第一種 2037-12-31 23:59:59(t2)

可以發現獲取出來的毫秒數和Integer.MAX_VALUE差不多就是1000倍的差距,
那不妨推斷一下這種說法的依據是在32位系統中存儲毫秒數,無溢出情況下的最大月末或者年末。
接下來通過Integer.MAX_VALUE構造出來的時間(t3)是2038-01-19 11:14:07.0 也驗證了我們的推斷。

第二種 9999-12-31 23:59:59

這個時間是我們在目前時間格式下的最大時間了,通過t21,t22的驗證發現通過,Timestamp類是可以存儲這種時間的。
再回過頭去看源代碼,發現用於存儲毫秒數的是Long而不是Integer,64位的Long完全可以到9999年嘛。

通過上面的驗證我們可以確認Timestamp類的最大時間可以是9999-12-31 23:59:59

Tips:
實際使用的時候我們的數據需要存儲到數據庫。
以mysql爲例,如果你想當然的將java的Timestamp直接就對應到數據庫的Timestamp,你會發現兩個數據類型範圍並不一樣。
摘抄一段mysql官方文檔的說明:

The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in ‘YYYY-MM-DD’ format. The supported range is ‘1000-01-01’ to ‘9999-12-31’. The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in ‘YYYY-MM-DD’ format. The supported range is ‘1000-01-01’ to ‘9999-12-31’.
The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in ‘YYYY-MM-DD HH:MM:SS’ format. The supported range is ‘1000-01-01 00:00:00’ to ‘9999-12-31 23:59:59’.
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC.


在真實使用的時候將業務,java類庫,數據庫數據類型進行對照,儘可能的找出符合要求的組合。

Equals並不相等

用到了時間,當然不能不比較了,看看下面這段代碼的輸出吧


Date d1 = new Date(0);
System.out.println(d1.equals(t1));//true
System.out.println(t1.equals(d1));//false

很有意思的結果,我們先來看看他是怎麼做到的吧,看源碼>>
Date.equals:


public boolean equals(Object obj) {	return obj instanceof Date && getTime() == ((Date) obj).getTime();}

由於Timestamp是繼承自Date,並且在強轉後高精度部分丟失導致getTime完全一致,所以第一個比較返回了true

Timestamp.equals:


public boolean equals(java.lang.Object ts) {
	if (ts instanceof Timestamp) {
		return this.equals((Timestamp)ts);	
		} else {
			return false;	}
			}

判斷Date並不是Timestamp類型,直接返回false;

代碼沒毛病,可是回到現實世界不就是說a = b 而b != a嗎,作爲一個嚴謹的程序員是不是總感覺哪裏不舒服呢?

相信作者也是這樣,先是在類上出現了這段說明

The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

然後方法上還要補上

Note: This method is not symmetric with respect to the equals(Object) method in the base class.

大意就是:我寫這個繼承關係只是實現繼承並不是類型繼承(爲了少寫點代碼,其實沒毛關係),equals方法和父類的equals方法不對等(你們用錯了我不負責啊)

總結

最後總結以下幾點
1, java.sql.Timestamp可以接受的時間範圍是1970-01-01 00:00:00 - 9999-12-31 23:59:59,具體使用的時候需要考慮時區帶來的偏移量
2, 和數據庫結合使用需要結合實際需求、數據庫數據類型的具體範圍選擇合適的範圍,不能僅僅通過名字來選擇數據類型
3, Timestamp類的equals方法和父類的equals方法並不是對等的,只有兩個比較的對象都是Timestamp的時候才能返回一致的結果
4, 我們在實際設計開發的時候儘量避免這種不舒服的設計,結合繼承,封裝,多態等多種手段確保我們設計的魯棒。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章