問題
最近維護老項目,遇到一個遺留的日期傳輸問題。A系統遠程調用B系統獲取申請時間字段,B系統接口返回的申請時間是String類型,結果A、B兩個系統顯示的申請時間不一樣,A系統的申請時間比B系統的提前了14個小時,問題可以通過簡單的代碼復現。
Date currentDate = new Date();
System.out.println("currentDate:" + currentDate);
String currentDateStr = currentDate.toString();
System.out.println("currentDateStr:" + currentDateStr);
long numberOfMilliseconds = currentDate.getTime();
System.out.println("numberOfMilliseconds:" + numberOfMilliseconds);
Date receivedCurrentDate = new Date(currentDateStr);
System.out.println("receivedCurrentDate:" + receivedCurrentDate);
Date correctCurrentDate = new Date(numberOfMilliseconds);
System.out.println("correctCurrentDate:" + correctCurrentDate);
輸出結果:
currentDate:Sun Aug 06 15:47:08 CST 2017
currentDateStr:Sun Aug 06 15:47:08 CST 2017
numberOfMilliseconds:1502005628392
receivedCurrentDate:Mon Aug 07 05:47:08 CST 2017
correctCurrentDate:Sun Aug 06 15:47:08 CST 2017
分析這個問題之前,先了解一些時間概念及關系。
格林尼治標準時間
格林尼治標準時間(Greenwich Mean Time,簡稱GMT)指位于英國倫敦郊區的皇家格林尼治天文臺當地的標準時間,因為本初子午線被定義為通過那里的經線。理論上來說,格林尼治標準時間的正午是指當太陽橫穿格林尼治子午線時(也就是在格林尼治上空最高點時)的時間。但由于地球在它的橢圓軌道里的運動速度不均勻,這個時刻可能與實際的太陽時有誤差,最大誤差達16分鐘。原因在于地球每天的自轉是有些不規則的,而且正在緩慢減速,因此格林尼治時間基于天文觀測本身的缺陷,已經不再被作為標準時間使用。現在的標準時間,是由原子鐘報時的協調世界時(UTC)來決定。
世界協調時
世界協調時(Coordinated Universal Time,簡稱UTC)又稱世界標準時間或世界協調時間,是最主要的世界時間標準,其以原子時秒長為基礎,在時刻上盡量接近于格林尼治標準時間。對于大多數用途來說,UTC時間被認為能與GMT時間互換,基本相等,但GMT時間已不再被科學界所確定。
CST
CST代表了4個不同的時間,每個時間和UTC之間的轉換關系如下:
中部標準時區(北美洲),Central Standard Time,UT-6:00
澳洲中部時間,Central Standard Time,UT+9:30
北京時間,China Standard Time,UT+8:00
古巴標準時間,Cuba Standard Time,UT-4:00
new Date()
public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
fastTime = date;
}
System.currentTimeMillis()返回當前時間與協調世界時1970年1月1日午夜之間的時間差(以毫秒為單位測量),使用的是UTC。直接輸出currentDate會調用toString方法,會根據所在的時區格式化輸出日期,所以輸出的是Sun Aug 06 15:47:08 CST 2017,格式化成CST時間。
輸出的numberOfMilliseconds是從1970年1月1日00:00:00 GMT以來,該Date對象表示的毫秒數,也就是獲取到的時間差。
new Date(currentDateStr):
@Deprecated
public Date(String s) {
this(parse(s));
}
public Date(long date) {
fastTime = date;
}
Date(String s)這個構造方法已經被標記為過時,不推薦使用。通過解析currentDateStr返回一個long類型的時間差(UTC時間)來構造日期對象,但這個解析的過程中,如果遇到CST,會被認為是指北美的時區,比格林威治時間晚6個小時,按照上面的轉換關系,轉成UTC需要加6個小時。而在輸出receivedCurrentDate的時候會調用toString方法,會根據所在的時區格式化輸出日期,也就是將UTC轉成China Standard Time,需要加8個小時,所以就出現了相差14個小時。
new Date(numberOfMilliseconds),這個構造方法因為中間沒有經過轉換,所以再次構造的日期是正確的。
總結
Date實現了java.io.Serializable,是可序列化的,可直接傳輸,不要傳輸其字符串數值。