您的位置:首页 > 编程语言 > Java开发

Java 时区问题解析

2017-11-14 10:54 507 查看


—-Java每个时间区域都有一个时间区域ID标识符,这个ID是个字符串,是由位于J2SE 安装程序的jre/lib子目录中的tzmappings文件保存这些ID列表。 J2SE 1.3 仅仅只包含tzmappings文件,但是 J2SE 1.4包含世界不同地区的时间区域数据文件。jre/lib/zi存放着这些文件。

—-TimeZone取默认值,先取该对象已经设定好的默认值,如果没有则取system.property中的”user.timezone”,再没有的话才根据“java.home”和“user.country”来获取。。。。(从TimeZone.getDefault()源码可知);

–System.property中user.timezone会在TimeZone执行由“java.home”和“user.country”获取timezone的方法后获得初值;

SimpleDateFormat使用

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
// 前台传进来的时间,默认是本地时区的时间,所以日期转换的时候,不会有变化
System.out.println("set default:" + TimeZone.getDefault());
Date dat = sdf.parse("2017-11-15");
System.out.println(dat);

//修改SimpleDateFormat的时区,这种方法修改时区不会更改系统的默认 --------------------时区,只是修改了转换类的使用时区
//前台传进来的时间,经转换类转换成指定时区的时间
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println("default:" + TimeZone.getDefault());
System.out.println("user set default:" + TimeZone.getTimeZone("GMT"));
Date dat1 = sdf.parse("2017-11-15");
System.out.println(dat1);
}


结果:

①set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
②Wed Nov 15 00:00:00 CST 2017
③default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
④user set default:sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
⑤Wed Nov 15 08:00:00 CST 2017


TimeZone/Calendar/Date/DateFormat

①TimeZone:
TimeZone对象给我们的是原始的偏移量,也就是与GMT相差的微秒数,即TimeZone表示时区偏移量,本质上以毫秒数保存与GMT的差值。获取TimeZone可以通过时区ID,如"America/New_York",也可以通过GMT+/-hh:mm来设定。例如北京时间可以表示为GMT+8:00。TimeZone.getRawOffset()方法可以用来得到当前时区的标准时间到GMT的偏移量。上段提到的"America/New_York"和"GMT+8:00"两个时区的偏移量分别为-18000000和28800000。
②Calendar
Calendar 类是一个抽象类,不能new,Calendar的getInstance()获取当前日历对象。
Calendar calendar = Calendar.getInstance();//获取当前日历对象
long unixTime = calendar.getTimeInMillis();//获取当前时区下日期时间对应的时间戳,和date.getTime()一样;
cal.getTime() 就是当前日期即相当于new date();
Calendar的getInstance()方法有参数为TimeZone和Locale的重载,可以使用指定时区和语言环境获得一个日历。无参则使用默认时区和语言环境获得日历。
③Date
计算机内部记录的时间(Date date = new Date()),  即  java.util.Date代表一个时间点,其值为距公元1970年1月1日 00:00:00的毫秒数。所以可以认为是没有时区和Locale概念的。但是通常我们把date的时区一定是当前操作系统的时区,String类型的时间可以通过转换类指定时区,但是最终转为date的时候,都需要将指定时区的时间转为系统所在时区的时间。
date.getTimeInMillis();//获取当前时区下日期时间对应的时间戳


Date date11 = new Date(1391174450000L); // 2014-1-31 21:20:50
String dateStr11 = "2014-1-31 21:20:50 ";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
Date dateTmp = dateFormat.parse(dateStr11);
System.out.println(dateTmp);
} catch (ParseException e) {

4000
e.printStackTrace();
}
String dateStrTmp = dateFormat.format(date11);
System.out.println(dateStrTmp);
结果:
Sat Feb 01 05:20:50 CST 2014
2014-01-31 13:20:50


④DateFormat:
日期格式化类DateFormat, 对于不同地区的配置一般有两个点, 一个是Locale , 一个是TimeZone。
前者(Locale)使DateFormat按所配置的地区特性来输出文字(例如中国,美国,法国不同地区对日期的表示格式不一样,中国可能是2001年10月5日)
后者(TimeZone)让DateFormat知道怎么去转换,去调整时间偏移度,从而得到符合配置的时区的时间.
GMT与UTC的时区是一样的,都是以伦敦时间为基准. 而GMT+8时区就是北京时间所在时区.同一时刻的时间比GMT快8小时。
注意事项:
Date.toString()和Calendar以及TimeZone依赖于TimeZone中defaultTimezone,即Calendar取得是TimeZone中设置的时区.
TimeZone中设置的时区由jre这个jar下的TimeZone的源码可知,先取用户自己设置的,没有的话再取system中的。


代码分析

package com.yu;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
* 关于时区的问题:
*  TimeZone中设置的时区由jre这个jar下的TimeZone的源码可知,先取用户自己设置的,没有的话再取system中的。
* 最好不要用TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))等设置timezone的时区,因为这会改变Jvm的时区,影响其他业务
* SimpleDateFormat 默认取得本地系统的时区,可以修改转换时候使用的时区
* @author yu<br>
* @version 1.0<br>
* @taskId <br>
* @CreateDate Nov 13, 2017 <br>
* @since V80<br>
* @see com.yu<br>
*/
public class TimeZoneUtil {
public TimeZoneUtil() {
}
public static void main (String[] args) {
notSetDefaultTimeZone();
setDefaultTimeZone();
testTime();
}
/**
*
* Description: 用户不设置TimeZone<br>
*
* @author yu.xiaoyan1<br>
* @taskId <br> <br>
*/
private static void notSetDefaultTimeZone() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
// 前台传进来的时间,先根据转换类的设置的时区转换日期(东八区),再转换为系统所在时区的日期。
System.out.println("set default:" + TimeZone.getDefault());
Date dat = sdf.parse("2017-11-15");
System.out.println(dat);

//修改SimpleDateFormat的时区,这种方法修改时区不会更改系统的默认时区,只是修改了转换类的使用时区
//前台传进来的时间,先根据转换类的设置的时区转换日期(GMT标准时区),再转换为系统所在时区的日期
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println("default:" + TimeZone.getDefault());
System.out.println("user set default:" + TimeZone.getTimeZone("GMT"));
Date dat1 = sdf.parse("2017-11-15");
System.out.println(dat1);
}
catch (ParseException e) {
e.printStackTrace();
}
Date date = new Date();
System.out.println("date.toString():" + date.toString());
System.out.println("date.getTime()" + date.getTime());

Calendar cal = Calendar.getInstance();
System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
Date date1 = new Date();
String dateStr = format.format(date1);
System.out.println(dateStr);

System.out.println("user set default:" + TimeZone.getDefault());

System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

System.out.println("system user.country:" + System.getProperty("user.country"));
System.out.println("system java.home:" + System.getProperty("java.home"));

System.out.println("默认时区:" + TimeZone.getDefault().getID());
System.out.println("--------------------------------------");
}
/**
*
* Description: 取用户设置的time zone<br>
*
* @author yu.xiaoyan1<br>
* @taskId <br> <br>
*/
private static void setDefaultTimeZone() {
System.out.println("After setTimeZone0:");
//设置TimeZone 默认的时区
TimeZone.setDefault(TimeZone.getTimeZone("GMT+2"));

Date date = new Date();
System.out.println("date.toString():" + date);
System.out.println("date.getTime()" + date.getTime());

Calendar cal = Calendar.getInstance();
System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
String dateStr = format.format(cal.getTime());
System.out.println(dateStr);

System.out.println("user set default:" + TimeZone.getDefault());

System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

System.out.println("system user.country:" + System.getProperty("user.country"));
System.out.println("system java.home:" + System.getProperty("java.home"));

System.out.println("默认时区:" + TimeZone.getDefault().getID());
System.out.println("--------------------------------------");
}
/**
*
* Description:TimeZone和系统user.timezone不一致的时候 ,取TimeZone设置的值<br>
*
* @author yu.xiaoyan1<br>
* @taskId <br> <br>
*/
private static void testTime() {

TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
System.setProperty("user.timezone", "GMT+2");
Date date = new Date();
System.out.println("date.toString():" + date);
System.out.println("date.getTime()" + date.getTime());
Calendar cal = Calendar.getInstance();
System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

System.out.println("user set default:" + TimeZone.getDefault());

System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

System.out.println("system user.country:" + System.getProperty("user.country"));
System.out.println("system java.home:" + System.getProperty("java.home"));

System.out.println("默认时区:" + TimeZone.getDefault().getID());
//.SimpleDateFormat.setTimeZone()并不会改变TimeZone的值
//既SimpleDateFormat.setTimeZone()又TimeZone.setDefault()时,SimpleDateFormat取SimpleDateFormat.setTimeZone()设定的值
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
date = new Date();
format.setTimeZone(TimeZone.getTimeZone("GMT+6"));
String dateStr = format.format(date);
System.out.println(dateStr);

System.out.println("user set default:" + TimeZone.getDefault());

System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

System.out.println("system user.country:" + System.getProperty("user.country"));
System.out.println("system java.home:" + System.getProperty("java.home"));

System.out.println("默认时区:" + TimeZone.getDefault().getID());
System.out.println("--------------------------------------");
}

}


运行结果:

date.toString():Tue Nov 14 10:46:32 CST 2017
date.getTime()1510627592734
Calendar.getInstance().getTime():Tue Nov 14 10:46:32 CST 2017
Calendar.getInstance().getTimeInMillis():1510627592767
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
2017-11-14 10:46:32 +0800
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:Asia/Shanghai
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
默认时区:Asia/Shanghai
--------------------------------------
After setTimeZone0:
date.toString():Tue Nov 14 04:46:32 GMT+02:00 2017
date.getTime()1510627592776
Calendar.getInstance().getTime():Tue Nov 14 04:46:32 GMT+02:00 2017
Calendar.getInstance().getTimeInMillis():1510627592777
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="GMT+02:00",offset=7200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
2017-11-14 04:46:32 +0200
user set default:sun.util.calendar.ZoneInfo[id="GMT+02:00",offset=7200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
system user.timezone:Asia/Shanghai
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
默认时区:GMT+02:00
--------------------------------------
date.toString():Tue Nov 14 10:46:32 CST 2017
date.getTime()1510627592778
Calendar.getInstance().getTime():Tue Nov 14 10:46:32 CST 2017
Calendar.getInstance().getTimeInMillis():1510627592778
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:GMT+2
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
默认时区:Asia/Shanghai
2017-11-14 08:46:32 +0600
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:GMT+2
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
默认时区:Asia/Shanghai
--------------------------------------


参考文献:

jvm 时区

时区

时区

时区

SImpleDateFormat的构造方法



夏令时:

将一个以字符串形式输入的北京时间转换成美国东部时间
String inputDate = "2011-05-14 23:30:00";
TimeZone timeZoneSH = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone timeZoneNY = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
inputFormat.setTimeZone(timeZoneSH);
Date date = null;
try
{
date = inputFormat.parse(inputDate);
}
catch (ParseException e)
{
}

SimpleDateFormat outputFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss Z yyyy", Locale.US);
outputFormat.setTimeZone(timeZoneSH);
System.out.println("Asia/Shanghai:" + outputFormat.format(date));
outputFormat.setTimeZone(timeZoneNY);
System.out.println("America/New_York:" + outputFormat.format(date));

那么,夏令时(DST)的问题怎么解决呢?令人高兴的是,JDK(or JRE)已自动为我们进行了夏令时处理。可以做个试验,来验证以上第2段代码能适用于夏令时转换。美国在2011年开始和结束夏令时的时间是:3.13 2AM和11.6 2AM。

1. 将输入时间inputDate设置为"2011-03-13 14:59:59",输出:
Asia/Shanghai:Sun Mar 13 14:59:59 +0800 2011
America/New_York:Sun Mar 13 01:59:59 -0500 2011
此时,美国东部时间还差1秒进入夏令时,与北京时间相差13小时。

2. 将输入时间inputDate设置为"2011-03-13 15:00:00",输出:
Asia/Shanghai:Sun Mar 13 15:00:00 +0800 2011
America/New_York:Sun Mar 13 03:00:00 -0400 2011
此时,美国东部时间刚好进入夏令时,与北京时间相差12小时,同时,所使用的时区也发生了变化。

结束夏令时的试验就不再赘述了。

JDK(or JRE)之所以能自动的进行DST处理,是因为其已内置了各个国家的夏令时政策,并提供Timezone Updater Tool来保持低版本JDK(or JRE)的TimeZone更新,但SUN官方推荐使用JDK(or JRE)的更新来更新TimeZone信息。

综上,我们应尽量在系统中使用如上描述的long类型变量来记录时间,借助相应的方法,可方便的格式化为不同时区的时间进行显示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: