前言
Java中的時間操作,也許大家都很熟悉,Date、Calendar、SimpleDateFormat等。但或許我們都只是會調用常見接口方法。今天解決的bug,關於TimeZone,即時區。
經常有人發現時間不對,比如相差8個小時等等,其真實原因便是TimeZone。只有正確合理的運用TimeZone,才能保證系統時間無論何時都是準確的。
影響TimeZone的因素:
- 操作系統的時區設置。
- 數據傳輸時時區設置。
第一個原因其實是根本原因,當數據在不同操作系統間流轉時,就有可能因爲操作系統的差異造成時間偏差,而JVM默認情況下獲取的就是操作系統的時區設置。因此在項目中最好事先設置好時區,例如:
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
那麼一旦時區設置不同導致時間有偏差怎麼辦?如何轉化呢?
用SimpleDateFormat的話,如下:
public static void main(String[] args) {
Date date = new Date(1359641834000L);// 2013-1-31 22:17:14
String dateStr = "2013-1-31 22:17:14";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
Date dateTmp = dateFormat.parse(dateStr);
System.out.println(dateTmp);
} catch (ParseException e) {
e.printStackTrace();
}
String dateStrTmp = dateFormat.format(date);
System.out.println(dateStrTmp);
}
運行結果:
Fri Feb 01 06:17:14 CST 2013 2013-01-31 14:17:14
我們發現同一時間,字符串和日期運行出來的結果並不相同,那麼我們應該怎麼理解呢?一切都要以當前操作系統的時間爲基準。我的操作系統是"Asia/Shanghai",即GMT+8的北京時間,那麼執行日期轉字符串的format方法時,由於日期生成時默認是操作系統時區,因此2013-1-31 22:17:14是北京時間,那麼推算到GMT時區,自然是要減8個小時的;而執行字符串轉日期的parse方法時,由於字符串本身沒有時區的概念,因此2013-1-31 22:17:14就是指GMT(UTC)時間【ps:所有字符串都看做是GMT時間】,那麼當轉化爲日期時要加上默認時區,即"Asia/Shanghai",因此要加上8個小時。
用Calendar的話,如下:
public static void main(String[] args) {
Date date = new Date(1359641834000L);// 2013-1-31 22:17:14
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE));
}
運行結果:
Thu Jan 31 22:17:14 CST 2013 14:17
Calendar不涉及到日期與字符串的轉化,因此不像SimpleDateFormat那麼複雜,與日期轉字符串的思路類似。但是需要注意的是,設置完時區後,我們不能用calendar.getTime()來直接獲取Date日期,因此此時的日期與一開始setTime時是相同值,要想獲取某時區的時間,正確的做法是用calendar.get()方法,那麼我們怎麼獲得Date類型的日期呢?
正確的做法如下:
public static void main(String[] args) {
Date date = new Date(1359641834000L);
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
System.out.println(calendar2.getTime());
}
運行結果:
Thu Jan 31 22:17:14 CST 2013 Thu Jan 31 14:17:14 CST 2013
完美通用轉換方法
其實上面兩個轉換方法都要受到操作系統的時區設置影響,如果軟件在不同操作系統運行,仍然會有時間誤差,那麼怎麼才能統一呢?
public static void main(String[] args) {
Date date = new Date(1359641834000L);
System.out.println(date);
date = changeTimeZone(date, TimeZone.getTimeZone("Asia/Shanghai"), TimeZone.getTimeZone("GMT"));
System.out.println(date);
}
/**
* 獲取更改時區後的日期
* @param date 日期
* @param oldZone 舊時區對象
* @param newZone 新時區對象
* @return 日期
*/
public static Date changeTimeZone(Date date, TimeZone oldZone, TimeZone newZone) {
Date dateTmp = null;
if (date != null) {
int timeOffset = oldZone.getRawOffset() - newZone.getRawOffset();
dateTmp = new Date(date.getTime() - timeOffset);
}
return dateTmp;
}
運行結果:
Thu Jan 31 22:17:14 CST 2013 Thu Jan 31 14:17:14 CST 2013
通過以上可以看出時區確實有不少需要我們注意和專研的地方,其實這並不是什麼高深的東西,也是java日期裏的基礎,盲目學習各種開源框架並不能成爲真正的高手,框架也是基於jdk的基礎上開發而來的,不懂jdk的基本知識,想真正提高到架構師的級別是非常苦難的,因此基礎很重要,掌握jdk基本功確實是成爲“大俠”的前提條件,共同努力吧!