一、TimeZone 簡介
TimeZone 表示時區位移量,也可以計算夏令時。
在操作 Date, Calendar等表示日期/時間的對象時,經常會用到TimeZone;因為不同的時區,時間不同。
下面說說TimeZone對象的 2種常用建立方式。
1.擷取預設的TimeZone對象
使用方法:
TimeZone tz = TimeZone.getDefault()
2.使用 getTimeZone(String id) 方法擷取TimeZone對象
使用方法:
// 擷取 “GMT+08:00”對應的時區TimeZone china = TimeZone.getTimeZone("GMT+:08:00");// 擷取 “中國/重慶”對應的時區TimeZone chongqing = TimeZone.getTimeZone("Asia/Chongqing");
關於 getTimeZone(String id) 這種方式支援的全部id參數的取值,可以通過以下方式尋找:
String[] ids = TimeZone.getAvailableIDs();for (String id:ids) System.out.printf(id+", ");
輸出結果:
Etc/GMT+12, Etc/GMT+11, Pacific/Midway, Pacific/Niue ....等等
例如,建立上面第2個列印值“Etc/GMT+11”對應的TimeZone。方法如下:
TimeZone tz = TimeZone.getTimeZone("Etc/GMT+11");TimeZone的函數介面// 建構函式
TimeZone():
Object clone()synchronized static String[] getAvailableIDs()synchronized static String[] getAvailableIDs(int offsetMillis)int getDSTSavings()synchronized static TimeZone getDefault()final String getDisplayName(Locale locale)String getDisplayName(boolean daylightTime, int style, Locale locale)final String getDisplayName()final String getDisplayName(boolean daylightTime, int style)String getID()abstract int getOffset(int era, int year, int month, int day, int dayOfWeek, int timeOfDayMillis)int getOffset(long time)abstract int getRawOffset()synchronized static TimeZone getTimeZone(String id)boolean hasSameRules(TimeZone timeZone)abstract boolean inDaylightTime(Date time)synchronized static void setDefault(TimeZone timeZone)void setID(String id)abstract void setRawOffset(int offsetMillis)abstract boolean useDaylightTime()
二、TimeZone樣本:
下面通過樣本示範在Date中使用TimeZone。
參考代碼如下(TimeZoneTest.java):
import java.text.DateFormat;import java.util.Date;import java.util.TimeZone;/** * TimeZone的測試程式 */public class TimeZoneTest { public static void main(String[] args) { // 測試建立TimeZone對象的3種方法 showUsageOfTimeZones() ; // 測試TimeZone的其它API testOtherAPIs() ; // 列印getTimeZone(String id)支援的所有id //printAllTimeZones() ; } /** * 測試建立TimeZone對象的3種方法 */ public static void showUsageOfTimeZones() { TimeZone tz; // (01) 預設時區 tz = TimeZone.getDefault(); printDateIn(tz) ; // (02) 設定時區為"GMT+08:00" tz = TimeZone.getTimeZone("GMT+08:00"); printDateIn(tz) ; // (03) 設定時區為"" tz = TimeZone.getTimeZone("Asia/Chongqing"); printDateIn(tz) ; } /** * 列印 tz對應的日期/時間 */ private static void printDateIn(TimeZone tz) { // date為2013-09-19 14:22:30 Date date = new Date(113, 8, 19, 14, 22, 30); // 擷取預設的DateFormat,用于格式化Date DateFormat df = DateFormat.getInstance(); // 設定時區為tz df.setTimeZone(tz); // 擷取格式化後的字串 String str = df.format(date); System.out.println(tz.getID()+" :"+str); } /** * 測試TimeZone的其它API */ public static void testOtherAPIs() { // 預設時區 TimeZone tz = TimeZone.getDefault(); // 擷取“id” String id = tz.getID(); // 擷取“顯示名稱” String name = tz.getDisplayName(); // 擷取“時間位移”。相對於“本初子午線”的位移,單位是ms。 int offset = tz.getRawOffset(); // 擷取“時間位移” 對應的小時 int gmt = offset/(3600*1000); System.out.printf("id=%s, name=%s, offset=%s(ms), gmt=%s\n", id, name, offset, gmt); } /** * 列印getTimeZone(String id)支援的所有id */ public static void printAllTimeZones() { String[] ids = TimeZone.getAvailableIDs(); for (String id:ids) { //int offset = TimeZone.getTimeZone(avaIds[i]).getRawOffset(); //System.out.println(i+" "+avaIds[i]+" "+offset / (3600 * 1000) + "\t"); System.out.printf(id+", "); } System.out.println(); }}
三、關於TimeZone和時間校準
涉及有關時間地區資訊時Java和Solaris很相似。每個時間地區都有一個時間地區ID標識符。在J2SE 1.3 and 1.4中,這個ID是個字串,是由位於J2SE 安裝程式的jre/lib子目錄中的tzmappings檔案這些ID列表。 J2SE 1.3 僅僅只包含tzmappings檔案,但是 J2SE 1.4包含世界不同地區的時間地區資料檔案。jre/lib/zi存放著這些檔案。在J2SE 1.4裡,sun.util.calendar.ZoneInfo從這些檔案擷取DST規則。在Solaris中, 這些時間地區資料檔案是以二進位形式存放的,不是文字檔,因此你不能看它們。 在J2SE 1.4中的時間地區資料檔案和在Solaris中是不同的。
java.util.TimeZone類中getDefault方法的原始碼顯示,它最終是會調用sun.util.calendar.ZoneInfo類的getTimeZone 方法。這個方法為需要的時間地區返回一個作為ID的String參數。這個預設的時間地區ID是從 user.timezone (system)屬性那裡得到。如果user.timezone沒有定義,它就會嘗試從user.country和java.home (System)屬性來得到ID。 如果它沒有成功找到一個時間地區ID,它就會使用一個"fallback" 的GMT值。換句話說, 如果它沒有計算出你的時間地區ID,它將使用GMT作為你預設的時間地區。
注意,System屬性是在java.lang.System類的initProperties方法中被初始化的。這是一個本地方法。因此原始碼是停用----除非你深入到J2SE分發包中的本地程式碼程式庫中去研究。然而,在Windows系統中,System 屬性是從Windows註冊表中被初始化的,而在Linux/Unix中是由環境變數來進行初始化。initProperties方法的Javadoc聲明,某些屬性"必須保證被定義" 且列出它們。被java.util.TimeZone類的getDefault方法使用的三個System屬性中,只有java.home作為一種“保證的”屬性在Javadoc中被列出。
推薦的解決方案 :
因此,你如何確保JAVA能給你正確的時間和日期呢?最好的辦法是確認JAVA虛擬機器(JVM)的預設TimeZone類是正確的,且是適合你的地理範圍(Locale)的。你如何來確保預設TimeZone是正確的且適合的呢?這又是一個新問題了。象大多數處理的問題一樣,這個也有許多解決方案。根據java.util.TimeZone.getDefault方法的原始碼來看,最好的辦法是正確地設定user.timezone屬性。在啟動JAVA虛擬機器時,你能很容易的通過使用 -D 命令 -line 參數的辦法來覆蓋(override)在java.lang.System.initProperties方法中所設定的值。例如:
java -Duser.timezone=Asia/Shanghai DateTest
這個命令啟動DateTest類,並設定 user.timezone屬性到Asia/Shanghai。你也能夠通過使用java.lang.System 類的setProperty方法來設定user.timezone 屬性:
System.setProperty("user.timezone","Asia/Shanghai");
如果沒有一個可用的時間地區ID適合你,那麼就你可以建立一個自訂TimeZone 使用java.util.TimeZone 類的 setDefault 方法將它設定為預設的時間地區----就象我先前在ItsInitializer 類中所做的操作一樣。
記住,在J2SE中,大多數日期和時間相關的類都包含時間地區資訊,包括那些格式類,如java.text.DateFormat, 因此它們都會被JVM的預設時間地區所影響。然而,在你建立這些類的執行個體時,你能為它們確保正確的時間地區資訊,使得你可以更容易來設定整個JVM的預設時間地區。並且一旦設定好,就可以確保所有的這些類都將使用同一個預設的時間地區。