详解Java中的时区类TimeZone的用法
一、TimeZone简介
TimeZone表示时区偏移量,也可以计算夏令时。
在操作Date,Calendar等表示日期/时间的对象时,经常会用到TimeZone;因为不同的时区,时间不同。
下面说说TimeZone对象的2种常用创建方式。
1.获取默认的TimeZone对象
使用方法:
TimeZonetz=TimeZone.getDefault()
2.使用getTimeZone(Stringid)方法获取TimeZone对象
使用方法:
//获取“GMT+08:00”对应的时区 TimeZonechina=TimeZone.getTimeZone("GMT+:08:00"); //获取“中国/重庆”对应的时区 TimeZonechongqing=TimeZone.getTimeZone("Asia/Chongqing");
关于getTimeZone(Stringid)这种方式支持的全部id参数的取值,可以通过以下方式查找:
String[]ids=TimeZone.getAvailableIDs(); for(Stringid:ids) System.out.printf(id+",");
输出结果:
Etc/GMT+12,Etc/GMT+11,Pacific/Midway,Pacific/Niue....等等例如,创建上面第2个打印值“Etc/GMT+11”对应的TimeZone。方法如下:
TimeZonetz=TimeZone.getTimeZone("Etc/GMT+11"); TimeZone的函数接口 //构造函数TimeZone():
Objectclone() synchronizedstaticString[]getAvailableIDs() synchronizedstaticString[]getAvailableIDs(intoffsetMillis) intgetDSTSavings() synchronizedstaticTimeZonegetDefault() finalStringgetDisplayName(Localelocale) StringgetDisplayName(booleandaylightTime,intstyle,Localelocale) finalStringgetDisplayName() finalStringgetDisplayName(booleandaylightTime,intstyle) StringgetID() abstractintgetOffset(intera,intyear,intmonth,intday,intdayOfWeek,inttimeOfDayMillis) intgetOffset(longtime) abstractintgetRawOffset() synchronizedstaticTimeZonegetTimeZone(Stringid) booleanhasSameRules(TimeZonetimeZone) abstractbooleaninDaylightTime(Datetime) synchronizedstaticvoidsetDefault(TimeZonetimeZone) voidsetID(Stringid) abstractvoidsetRawOffset(intoffsetMillis) abstractbooleanuseDaylightTime()
二、TimeZone示例:
下面通过示例演示在Date中使用TimeZone。
参考代码如下(TimeZoneTest.java):
importjava.text.DateFormat; importjava.util.Date; importjava.util.TimeZone; /** *TimeZone的测试程序 */ publicclassTimeZoneTest{ publicstaticvoidmain(String[]args){ //测试创建TimeZone对象的3种方法 showUsageOfTimeZones(); //测试TimeZone的其它API testOtherAPIs(); //打印getTimeZone(Stringid)支持的所有id //printAllTimeZones(); } /** *测试创建TimeZone对象的3种方法 */ publicstaticvoidshowUsageOfTimeZones(){ TimeZonetz; //(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对应的日期/时间 */ privatestaticvoidprintDateIn(TimeZonetz){ //date为2013-09-1914:22:30 Datedate=newDate(113,8,19,14,22,30); //获取默认的DateFormat,用于格式化Date DateFormatdf=DateFormat.getInstance(); //设置时区为tz df.setTimeZone(tz); //获取格式化后的字符串 Stringstr=df.format(date); System.out.println(tz.getID()+":"+str); } /** *测试TimeZone的其它API */ publicstaticvoidtestOtherAPIs(){ //默认时区 TimeZonetz=TimeZone.getDefault(); //获取“id” Stringid=tz.getID(); //获取“显示名称” Stringname=tz.getDisplayName(); //获取“时间偏移”。相对于“本初子午线”的偏移,单位是ms。 intoffset=tz.getRawOffset(); //获取“时间偏移”对应的小时 intgmt=offset/(3600*1000); System.out.printf("id=%s,name=%s,offset=%s(ms),gmt=%s\n", id,name,offset,gmt); } /** *打印getTimeZone(Stringid)支持的所有id */ publicstaticvoidprintAllTimeZones(){ String[]ids=TimeZone.getAvailableIDs(); for(Stringid:ids){ //intoffset=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标识符。在J2SE1.3and1.4中,这个ID是个字符串,是由位于J2SE安装程序的jre/lib子目录中的tzmappings文件这些ID列表。J2SE1.3仅仅只包含tzmappings文件,但是J2SE1.4包含世界不同地区的时间区域数据文件。jre/lib/zi存放着这些文件。在J2SE1.4里,sun.util.calendar.ZoneInfo从这些文件获取DST规则。在Solaris中,这些时间区域数据文件是以二进制形式存放的,不是文本文件,因此你不能看它们。在J2SE1.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/ShanghaiDateTest
这个命令启动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的默认时间区域。并且一旦设置好,就可以确保所有的这些类都将使用同一个默认的时间区域。