Java時區切換時的需要注意

前言

Java中的時間操作,也許大家都很熟悉,Date、Calendar、SimpleDateFormat等。但或許我們都只是會調用常見接口方法。今天解決的bug,關於TimeZone,即時區。

經常有人發現時間不對,比如相差8個小時等等,其真實原因便是TimeZone。只有正確合理的運用TimeZone,才能保證系統時間無論何時都是準確的。

影響TimeZone的因素:

  1. 操作系統的時區設置。
  2. 數據傳輸時時區設置。

第一個原因其實是根本原因,當數據在不同操作系統間流轉時,就有可能因爲操作系統的差異造成時間偏差,而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基本功確實是成爲“大俠”的前提條件,共同努力吧!

發佈了81 篇原創文章 · 獲贊 15 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章