Android與伺服器通訊:如何保證兩端時間一致性
在AChat專案的開發過程中,專案要求無論終端是什麼時區設定、地處何方,終端的時間是否正確,post到伺服器的資料包裡面的時間欄位均要求跟伺服器同步,也就是說,使用者買來一部新手機、新平板,不做任何日期時間、時區方面的設定,裝了App就用,也能讓時間資料正確。
我是這樣設計的,在app的setting中有3個變數org_tablet_tm,org_server_tm和server_timezone,App啟動的時候,即聯線伺服器取回當時的時間和伺服器所在時區分別儲存在org_server_tm和server_timezone,同一刻,取終端的時間儲存在org_tablet_tm。
定義now()函式,此函式取當前裝置時間再加上org_server_tm-org_tablet_tm的差值。
/**
* 永遠以server為準
* @return
*/
public static Date now(){
Date w_ret=new Date();
w_ret.setTime(w_ret.getTime()+(org_server_tm-org_tablet_tm));
return w_ret;
}
好了,這時候儘管裝置終端的時間亂七八糟,只要時區跟伺服器時區一致,用now()函式即可獲得伺服器上此時此刻的時間。
但是,問題來了,每個使用者的終端裝置時區不一定跟伺服器上的一致,有可能他們根本沒調時間、或沒勾上自動同步時間,這時候需要我們用程式碼實現在終端不同時區也能跟伺服器上的時間同步。
先看看這個DateTimeConvertToServer這個函式,先從tm中減去當前時區跟格林威治之間的時間差,再加上伺服器所在時區跟格林威治時間差,即可算出到伺服器時間:
/*** * 把當地的時間轉為伺服器當時的時間,一般遞交資料前先把Date一類資料轉換 * @param tm * @return */ public static Date DateTimeConvertToServer(Date tm){ if (tm==null)return null; tm=new Date(tm.getTime()-getDiffTimeZoneRawOffsetStd(TimeZone.getDefault().getID())); //轉成格林威治時間 Date d=new Date(tm.getTime()+getDiffTimeZoneRawOffsetStd(server_timezone)); return d; }
再看getDiffTimeZoneRawOffsetStd函式,用於計算指定時區跟格林威治時區的時間差(毫秒):
/***
* 計算出指定時區跟格林威治時間差
* @param timeZoneId
* @return
*/
public static int getDiffTimeZoneRawOffsetStd(String timeZoneId) {
//return TimeZone.getTimeZone(timeZoneId).getRawOffset();
TimeZone tz = TimeZone.getTimeZone(timeZoneId);
return tz.getOffset(GregorianCalendar.getInstance(tz).getTimeInMillis());
}
我在引數裡面配置一個選項”是否轉換成終端時間“,若轉換,則什麼都不做,因為Android系統能根據當前設定的時區自動轉換時間,若不轉換,則顯示伺服器端時間,則需要用DateTimeConvertToServer函式轉換一下。
補充一下截圖,當前平板時間亂套,時區是巴庫asia/baku,伺服器是東8區,下面是平板截圖:
伺服器上儲存的資料:
==================================
完結前吐槽一下,網路上流傳一段時區差的計算函式,它們用TimeZone的getRawOffset,當時我也抄來用,各種時區試一遍,開始是各種適合,但測試到Asia/Baku時頓時石化了,同樣是東5區的卡拉奇、烏拉爾都沒問題,自巴庫起開始一路往西的時區都相差一小時!!!用getRawOffset獲得巴庫離格林威治差4小時,但應該是5小時!!由於時間問題沒仔細分析什麼原因,有空再看並。
==================================
然而更狗血的事件發生了,在公司做的一批平板上執行APP,發現 android 4.2的老平板, 預設的American/New York按上述方法轉為伺服器的東8區時間問題要快1小時, 在android 4.4.2上則沒有這個問題! 後來除錯發現American/New York時區在這平板上相差GMT -5 ! 而在我的華為手機上為GMT -4!
不明白為什麼會這樣! 但換另一種做法,就是設定平板預設與伺服器上的相同無需手動計算差值,則所有問題得到解決:
if (App.server_timezone!=null){
TimeZone.setDefault(TimeZone.getTimeZone(App.server_timezone));
}