2014年1月6日 星期一

對於 Java Date 類別的小測試

在打完關於 Joda-Time 心得之後,突然心中產生一個疑問。Java 原生的 Date 類別,輸出時候既然包含了時區的資訊,那麼是不是可以經過某一種設定,達到轉換時區的目的?在寫了一些程式測試之後,發現某些層面上來看是可行的,只不過其可用性還是得視當時的環境而定。

測試的方式,一樣使用固定的字串作為時間的參數,並且以 System.out.println() 方便觀察結果。基於觀察原生 Java 類別的目的,所以完全不使用 Joda-Time 相關類別,下面是最開始的程式…
public static void main(String[] args) {
  try {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
    Date date = sdf.parse("2013-12-26 18:48:24 UTC");
    System.out.println(date);
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

輸出…
Fri Dec 27 02:48:24 CST 2013
從這裡可以發現到在解析的過程中,時區參數是被納入考量的。因此 UTC(+0)的時間在輸出時被加了 8 個小時,成為中原標準時間,這就達到某種程度的轉換效果。不過這種作法的問題,在於時區的表示其實並非唯一。以中原標準時間(China Standard Time)的縮 CST 為例,也可能代表了澳洲中部時間(Central Standard Time)或 中部標準時區(北美洲),Central Standard Time (North America)甚至是 古巴標準時間 Cuba Standard Time。再加上由資料庫取出的時間字串不見得帶有這樣的縮寫,因此,還是把字串裡的時區資訊拿掉,程式改成這樣…
public static void main(String[] args) {
  try {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = sdf.parse("2013-12-26 18:48:24");
    System.out.println(date);
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

輸出…
Thu Dec 26 18:48:24 CST 2013
這樣就是直接把字串轉時間,沒有任何轉換的效果。

查了官方的文件,發現 Date 類別並沒有設定時區方法,而 Calendar 類別所作的,也只有在解析上可以設定時區,就和第一個例子一樣,最終還是在輸出的時候被轉換為系統的時區。再研究一陣子發現,有個 TimeZone 類別帶有 setDefault 的方法,似乎可以進行調整,因此把程式改成下面這樣…
public static void main(String[] args) {
  try {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC")); // 多加了這一行
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = sdf.parse("2013-12-26 18:48:24");
    System.out.println(date);
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

輸出…
Thu Dec 26 18:48:24 UTC 2013
發現輸出的時區改變了,那麼,如果在解析完字串之後,再改變時區。是不是就達到改變時區的效果呢?再多加了兩行程式看看…
public static void main(String[] args) {
  try {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = sdf.parse("2013-12-26 18:48:24");
    System.out.println(date);
    TimeZone.setDefault(TimeZone.getTimeZone("Asia/Taipei")); // 對時區再作一次轉換
    System.out.println(date);  // 再次輸出結果作為比較
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

輸出…
Thu Dec 26 18:48:24 UTC 2013
Fri Dec 27 02:48:24 CST 2013
由結果可以看得出來,不單單是時區改變了,連時間也變成 +8 小時的中原標準時間。猜想 Date 物件本身可能不帶有時區資訊,只有在輸出的時候才會參考 TimeZone 類別裡的設定值。因此,在解析時間的前後去調整預設時區的設定,就可以簡單的作到不同時區時間轉換的效果。

我認為,如果是單獨執行的程式,雖然這種作法滿瞎的,但也不失為一個轉換時間的方法。但是情境變成在 Servlet 上運作,如果在多個網路要求同時被處理的狀態下改變 TimeZone 的設定,那就可能導致在另一個執行緒裡的時間解析或輸出異常,因而不可被使用。我想,這種無法被使用在不同狀況的結果,可能是其中一個讓 Joda-Time 的作者覺得原生的 Date 相關類別設計得很蠢的原因吧!

沒有留言:

張貼留言