加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

基于java时区转换夏令时的问题及解决方法

发布时间:2020-05-23 16:02:03 所属栏目:Java 来源:互联网
导读:一.准备知识1.America/New_York的夏令时时间如下:包左不包右2016-3-13,02:00:00到2016-11-6,02:00:00

一.准备知识

1.America/New_York的夏令时时间如下: 包左不包右

2016-3-13,02:00:00到2016-11-6,02:00:00 

2017-3-12,02:00:00到2017-11-5,02:00:00

2.三字母时区 ID

为了与 JDK 1.1.x 兼容,一些三字母时区 ID(比如 "PST"、"CTT"、"AST")也受支持。

但是,它们的使用被废弃,这是因为相同的缩写经常用于多个时区

例如 CST:有4个意思,美国,澳大利亚,中国,古巴时间

3.标准

GMT:Green Mean Time格林威治标准时间,1960年前被作为标准时间参考 GMT+12-->GMT-12

java8的范围为GMT+18-->GMT-18

UTC:Coordinated Universal Time 时间协调世界时间 ,比GMT精确,在1972年1月1日成为新的标准;UTC,UTC+1,UTC+2...UTC+12,UTC-12...UTC-1

java8的范围 UTC-18-->UTC+18

DST:Daylight Saving Time 夏令时间,指在夏天的时候,将时钟拨快一个小时,以提早日光的使用,在英国称为夏令时间;

目前有110多个国家采用夏令时;

在中国,从1986-1992只实行了6年,之后就取消了;原因如下:

1.中国东西方向跨度很大,而且采用的是统一的东八区,采用夏令时无法兼容东西部;

2.高纬度地区,冬夏昼夜时间变化大;意义不大;

4.表示东八区可以用 : GMT+8或者Etc/GMT-8(刚好相反,为什么呢,因为php开发者认为,东八区比标准时间快8小时,应该减去8小时,于是表示成了这样。参考的对象不同导致了不同的表示方法;)

5. 中国时区的表示方式

GMT+8
UTC+8
Asia/Harbin 哈尔滨 //中国标准时间
Asia/Chongqing 重庆//中国标准时间
Asia/Chungking 重庆//中国标准时间
Asia/Urumqi 乌鲁木齐//中国标准时间
Asia/Shanghai 上海(东8区)//中国标准时间
PRC
Asia/Macao 澳门 //中国标准时间
Hongkong 香港 //香港时间跟中国标准时间一致
Asia/Hong_Kong 香港
Asia/Taipei 台北(台湾的) //中国标准时间
新加坡跟中国的时间一样;
Asia/Singapore
Singapore

6. 标准时区的表示

UTC
UTC+0
UTC-0
GMT 格林尼治标准时间
GMT0 格林尼治标准时间
Etc/GMT 格林尼治标准时间
Etc/GMT+0 格林尼治标准时间
Etc/GMT-0 格林尼治标准时间
Etc/GMT0 格林尼治标准时间
注意:GMT+xx(-xx)有很大的包容性,还可以自动的识别各种时间的表示

二. 时区转换

环境:java8之前

1.将当前时间转换为指定时区显示

@Test 
public void test() throws Exception { 
 Date a=new Date(); 
 SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
 sf.setTimeZone(TimeZone.getTimeZone("America/New_York")); 
 //把中国时区转为了美国纽约时区 
 System.out.println(sf.format(a)); 
} 

2.指定时间转为指定时区显示

真能正确转换吗?好像有个坑,看了看网上的实现

关于夏令时,感觉有点问题

//实现方式1 没有考虑夏令时 
 @Test 
public void test2() throws Exception { 
/  Date dateTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 14:00:00"); 
 Date dateTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-10-6 14:00:00"); 
 TimeZone zhong = TimeZone.getTimeZone("GMT+8:00");//中国 
 TimeZone york = TimeZone.getTimeZone("America/New_York"); //GMT-5 
 //这里的时区偏移量是固定的,没有夏令时,错 
 long chineseMills = dateTime.getTime() + york.getRawOffset()-zhong.getRawOffset(); 
 Date date = new Date(chineseMills); 
 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); 
} 
 //实现方式2 你可能回想,用Calendar类的Calendar.DST_OFFSET
 //还是不对Calendar.DST_OFFSET这个是死的
	@Test
	public void test3() throws Exception {

//		Date time =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 14:00:00");
//		Date time =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 1:00:00");
//		Date time =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 0:59:00");
//		Date time =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 1:59:59");
		Date time =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-11-6 3:00:00");
	
		// 1、取得本地时间:
		Calendar cal = Calendar.getInstance();
		cal.setTime(time);
		cal.setTimeZone(TimeZone.getTimeZone("America/New_York"));
		// 2、取得时间偏移量:这个是固定的
		int zoneOffset = cal.get(Calendar.ZONE_OFFSET)/(1000*60*60);
		// 3、取得夏令时差:这个是固定的,不是根据时间动态判断,只要时区存在夏令时,就是1
		int dstOffset = cal.get(Calendar.DST_OFFSET)/(1000*60*60);
		
		System.out.println(zoneOffset);
		System.out.println(dstOffset);
		// 4、从本地时间里扣除这些差量,即可以取得UTC时间:
//		cal.add(Calendar.MILLISECOND,-(zoneOffset + dstOffset));
		cal.add(Calendar.HOUR,-(zoneOffset + dstOffset));
		Date time2 = cal.getTime();
		System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time2));
		}
//实现方式3 

准备工作 
  // 不是说java会自动替我们处理夏令时吗
	//先来个简单的测试,看夏令时判断方法是否是正确,就可以推断,java的自动处理是否正确
	//已知2016年:America/New_York的夏令时时间是: 2016-3-13 02:00:00 到 2016-11-06 01:59:59 
  @Test
 	public void test4() throws Exception {
 	//转换为0时区时间作为参照点
 	SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 	sf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
//  	Date dateTime=sf.parse("2016-11-6 5:59:59");
 	Date d1=sf.parse("2016-03-13 6:59:59");//false 
  	Date d2=sf.parse("2016-03-13 7:00:00");//true 
  	Date d3=sf.parse("2016-11-6 6:59:59");//false 
  	Date d4=sf.parse("2016-11-6 7:00:00");//false
  	//现在发现了,对于夏令时开始的时间判断确实没问题,但是对于夏令时的结束时间判断错误,准确说,提前了一个小时判断了
  	//看下面验证就知道了,那么为什么提前了一个小时,不知道,怎么解决? 目前想到的只能用java8
  d3=sf.parse("2016-11-6 5:59:59");//true 
  d4=sf.parse("2016-11-6 6:00:00");//false 
  TimeZone york = TimeZone.getTimeZone("America/New_York"); //GMT-5
  	
  System.out.println("目标时区是否使用了夏令时:"+isDaylight(york,d1));
  System.out.println("目标时区是否使用了夏令时:"+isDaylight(york,d2));
  System.out.println("目标时区是否使用了夏令时:"+isDaylight(york,d3));
  System.out.println("目标时区是否使用了夏令时:"+isDaylight(york,d4));
 	}
 
 //判断是否在夏令时
 private boolean isDaylight(TimeZone zone,Date date) {
		return zone.useDaylightTime()&&zone.inDaylightTime(date);
 }
 //实现方式3 
 //通过上面的验证我们知道了系统的判断是有问题的,通过设置时区,程序自动处理夏令时也不是那么正确,起码我现在无法理解为什么
  @Test
	public void test5() throws Exception {
 	//中间相隔13个小时  中国+8 纽约-5
 	ChangeZone("2016-3-13 14:59:59","PRC","America/New_York","yyyy-MM-dd HH:mm:ss");//2016-03-13 01:59:59
 	ChangeZone("2016-3-13 15:00:00","yyyy-MM-dd HH:mm:ss");//2016-03-13 03:00:00
 	ChangeZone("2016-11-6 13:59:59","yyyy-MM-dd HH:mm:ss");//2016-11-06 01:59:59
  //这个结果是不对的,应该02:00:00
  ChangeZone("2016-11-6 14:00:00","yyyy-MM-dd HH:mm:ss");//2016-11-06 01:00:00
 }
 	//具体的实现如下:
 //思路是没问题的
 public static void ChangeZone(String time,String srcID,String destID,String pattern) throws ParseException {
 	 //设置默认时区
   TimeZone zone = TimeZone.getTimeZone(srcID);
 	 TimeZone.setDefault(zone);
 	 Date date = new SimpleDateFormat(pattern).parse(time);
 	//设置目标时区
 	TimeZone destzone = TimeZone.getTimeZone(destID);
 	SimpleDateFormat sdf = new SimpleDateFormat(pattern);
 	//设置要格式化的时区
 	sdf.setTimeZone(destzone);
 	String changTime = sdf.format(date);
 	// 获取目标时区
 	System.out.println("修改时区后" + destzone.getID() + "的时间:" + changTime);
  }

小结:以上的三种实现方式得到的结果对夏令时都有点问题

三,java8的实现

1.先看看java8的支持时区变化

//jdk8的可用时区
	@Test
	public void testName1() throws Exception {
		//jdk8的所有时区
		Set<String> ids = ZoneId.getAvailableZoneIds();
		String[] id1 = ids.toArray(new String[ids.size()]);
		String idss = Arrays.toString(id1).replace("]",",]");
		
		System.out.println(ids.size());//少了28个 595
		
		//jdk8之前的所有时区
		String[] id2 = TimeZone.getAvailableIDs();
		System.out.println(id2.length); //623
	
		//找出没jdk8中没有的
		for (String id : id2) {
			if (!idss.contains(id+",")) {
				System.out.print(id+",");
			}
		}
		
		//结论:jdk8里面的所有时区,在之前 全部都有
		//jdk8删除的时区如下:
		//都是一些容易引起歧义的时区表示方法
//		 EST,HST,MST,ACT,AET,AGT,ART,AST,BET,BST,CAT,CNT,CST,CTT,EAT,ECT,IET,IST,JST,MIT,NET,NST,PLT,PNT,PRT,PST,SST,VST,// 但是这些短名称的其实还是可以使用的,只是支持列表里面没有了而已
		LocalDateTime date = LocalDateTime.ofInstant(Instant.now(),ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("nDate = " + date);
		
	}

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读