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类型变量来记录时间,借助相应的方法,可方便的格式化为不同时区的时间进行显示。
相关文章推荐
- JAVA基础:Java多语言编码问题解析
- Java中String类型的参数传递问题的解析
- Java 时区问题
- Java中String类型的参数传递问题的解析
- 中文化和国际化问题权威解析之三:Java中文问题分析
- Java时区问题
- JAVA基础:Java多语言编码问题解析
- 在Spring中发现java.lang.ClassCastException: $Proxy1问题解析
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之二:Java国际化基础
- java设置时区问题
- 中文化和国际化问题权威解析之二:Java国际化基础
- Java多语言编码问题解析(2)
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 解析 Java 类和对象的初始化过程(由一个单态模式引出的问题谈起)
- Java问题解析:到底创建了几个String对象?
- Java多语言编码问题解析
- Java中XML文档的解析问题(已解决)
- JAVA基础:Java多语言编码问题解析
- 基于java时区转换夏令时的问题及解决方法