2016年4月25日 星期一

Java : SimpleDateFormat、TimeZone和Date -- 觀念釐清

在這篇裡主要紀錄了在Java中,對於SimpleDateFormat、TimeZone和Date的理解,釐清之前對它們有誤會及不太清楚的地方。

java.util.Date是一個早期Java中專門用來紀錄日期的一個類別,同時也是常用java.sql.Date的父類別,它並沒有儲存時區的資訊,當我們要對其進行不同時區的值(String或Date)轉換時,可以利用SimpleDateFormat及TimeZone來幫助我們。

TimeZone是個儲存時區資訊的類別,而SimpleDateFormat是一個用來進行日期格式轉換的類別,可以利用SimpleDateFormat.setTimeZone()來設定TimeZone,而常用的為以下兩個方法:


  1. String SimpleDateFormat.format(Date)
  2. Date SimpleDateFormat.parse(String)

SimpleDateFormat.format()可以送入一個Date物件,並返回一個表示日期的String。
如果SimpleDateFormat有設定TimeZone的話,它會將送入的Date參數當成其TimeZone為local的TimeZone ( 等同TimeZone.getDefault() ),並將它轉成用SimpleDateFormat設定的TimeZone來看時,正確的日期String。

SimpleDateFormat.parse(String)可以送入一個表式日期的String,並返回一個Date物件。
如果SimpleDateFormat有設定TimeZone的話,它會將送入的String表示的日期當成其TimeZone為SimpleDateFormat設定的TimeZone,並將它轉成用local TimeZone來看時,正確的日期Date物件。

========================
接著下面舉一個例子:

如果我們有一個表示Local TimeZone日期的Date物件,例如:
System.our.println(dateObject_local);   //2016/4/25 16:47:59


我們想把它轉成在TimeZone為America/Los_Angeles時,看這個日期應看到的Date物件,例如
System.our.println(dateObject_America);   //2016/4/25 01:47:59


應該要如何做呢?
首先宣告TimeZone並設定給SimpleDateFormat
TimeZone timeZone_America = TimeZone.getTimeZone("America/Los_Angeles");
TimeZone timeZone_default = TimeZone.getDefault();

SimpleDateFormat simpleDateFormat_America = new SimpleDateFormat();
simpleDateFormat_America.setTimeZone(timeZone_America);

SimpleDateFormat simpleDateFormat_Taiwan = new SimpleDateFormat();
simpleDateFormat_Taiwan.setTimeZone(timeZone_default);

Date dateObject_local = new Date();
System.out.println("dateObject_local:" + dateObject_local);  //dateObject_local:Mon Apr 25 18:11:27 CST 2016

接著我們用simpleDateFormat_America去format上面那個Date,
String dateString_America = simpleDateFormat_America.format(dateObject_local);
System.out.println("dateString_America:" + dateString_America); //dateString_America:2016/4/25 上午 3:11

會看到時間被減了15小時,這正是美國比台灣慢15小時的時差。
如果我們想得到輸出是 3:11的Date物件,下面這樣是錯的:
Date Wrong_dateObject_America = simpleDateFormat_America.parse(dateString_America);
System.out.println("Wrong_dateObject_America:" + Wrong_dateObject_America); //Wrong_dateObject_America:Mon Apr 25 18:16:00 CST 2016

原因是當用simpleDateFormat_America去parse上面那個String, dateString_America時,它會用simpleDateFormat_America設定的TimeZone去看,也就是說,它認為這是美國時間3:11,所以要parse回local time的Date時,它會將時間加15小時,所以得到非我們預期的結果。

正確的做法是下面這樣:
Date Correct_dateObject_America = simpleDateFormat_Taiwan.parse(dateString_America);
System.out.println("Correct_dateObject_America:" + Correct_dateObject_America); //Correct_dateObject_America:Mon Apr 25 03:16:00 CST 2016

我們用simpleDateFormat_Taiwan去parse,讓它以為這是本地時間3:11,再parse成local time的Date時,就不會進行時差的動作了(因為時差為0)。

只要記得,Date本身沒有時區的資訊,基本上都用Local TimeZone去對待,不管是被format還是由parse得到。

而String則會用SimpleDateFormat設定的TimeZone去對待,不管是由format得到還是被parse。

沒有留言 :

張貼留言