前言

Java中的时间操作,也许大家都很熟悉,Date、Calendar、SimpleDateFormat等。但或许我们都只是会调用常见接口方法。今天解决的bug,关于TimeZone,即时区。

经常有人发现时间不对,比如相差8个小时等等,其真实原因便是TimeZone。只有正确合理的运用TimeZone,才能保证系统时间无论何时都是准确的。

影响TimeZone的因素:

  1. 操作系统的时区设置。
  2. 数据传输时时区设置。

第一个原因其实是根本原因,当数据在不同操作系统间流转时,就有可能因为操作系统的差异造成时间偏差,而JVM默认情况下获取的就是操作系统的时区设置。因此在项目中最好事先设置好时区,例如:

TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));

那么一旦时区设置不同导致时间有偏差怎么办?如何转化呢?

用SimpleDateFormat的话,如下:

	public static void main(String[] args) {
Date date = new Date(1359641834000L);// 2013-1-31 22:17:14
String dateStr = "2013-1-31 22:17:14";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
Date dateTmp = dateFormat.parse(dateStr);
System.out.println(dateTmp);
} catch (ParseException e) {
e.printStackTrace();
}
String dateStrTmp = dateFormat.format(date);
System.out.println(dateStrTmp);
}

运行结果:

Fri Feb 01 06:17:14 CST 2013
2013-01-31 14:17:14

我们发现同一时间,字符串和日期运行出来的结果并不相同,那么我们应该怎么理解呢?一切都要以当前操作系统的时间为基准。我的操作系统是"Asia/Shanghai",即GMT+8的北京时间,那么执行日期转字符串的format方法时,由于日期生成时默认是操作系统时区,因此2013-1-31 22:17:14是北京时间,那么推算到GMT时区,自然是要减8个小时的;而执行字符串转日期的parse方法时,由于字符串本身没有时区的概念,因此2013-1-31 22:17:14就是指GMT(UTC)时间【ps:所有字符串都看做是GMT时间】,那么当转化为日期时要加上默认时区,即"Asia/Shanghai",因此要加上8个小时。

用Calendar的话,如下:

	public static void main(String[] args) {
Date date = new Date(1359641834000L);// 2013-1-31 22:17:14
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE));
}

运行结果:

Thu Jan 31 22:17:14 CST 2013 14:17

Calendar不涉及到日期与字符串的转化,因此不像SimpleDateFormat那么复杂,与日期转字符串的思路类似。但是需要注意的是,设置完时区后,我们不能用calendar.getTime()来直接获取Date日期,因此此时的日期与一开始setTime时是相同值,要想获取某时区的时间,正确的做法是用calendar.get()方法,那么我们怎么获得Date类型的日期呢?

正确的做法如下:

	public static void main(String[] args) {
Date date = new Date(1359641834000L);
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
System.out.println(calendar2.getTime());
}

运行结果:

Thu Jan 31 22:17:14 CST 2013
Thu Jan 31 14:17:14 CST 2013

完美通用转换方法

其实上面两个转换方法都要受到操作系统的时区设置影响,如果软件在不同操作系统运行,仍然会有时间误差,那么怎么才能统一呢?

	public static void main(String[] args) {
Date date = new Date(1359641834000L);
System.out.println(date);
date = changeTimeZone(date, TimeZone.getTimeZone("Asia/Shanghai"), TimeZone.getTimeZone("GMT"));
System.out.println(date);
} /**
* 获取更改时区后的日期
* @param date 日期
* @param oldZone 旧时区对象
* @param newZone 新时区对象
* @return 日期
*/
public static Date changeTimeZone(Date date, TimeZone oldZone, TimeZone newZone) {
Date dateTmp = null;
if (date != null) {
int timeOffset = oldZone.getRawOffset() - newZone.getRawOffset();
dateTmp = new Date(date.getTime() - timeOffset);
}
return dateTmp;
}

运行结果:

Thu Jan 31 22:17:14 CST 2013
Thu Jan 31 14:17:14 CST 2013

通过以上可以看出时区确实有不少需要我们注意和专研的地方,其实这并不是什么高深的东西,也是java日期里的基础,盲目学习各种开源框架并不能成为真正的高手,框架也是基于jdk的基础上开发而来的,不懂jdk的基本知识,想真正提高到架构师的级别是非常苦难的,因此基础很重要,掌握jdk基本功确实是成为“大侠”的前提条件,共同努力吧!

Java时区切换时的需要注意的更多相关文章

  1. 让多个Fragment 切换时不重新实例化、FragmentTabHost切换Fragment时避免UI重新加载

    http://www.tuicool.com/articles/FJ7VBb FragmentTabHost切换Fragment时避免UI重新加载 不过,初次实现时发现有个缺陷,每次FragmentT ...

  2. 时区切换导致quartz定时任务没有触发问题

    时区切换对Quartz的cron表达式有影响,切换的1小时内停止触发定时任务,导致sla没有定时清空内存计数,误发限流. 美国夏令时PST切换到冬令时PDT,会有时间跳变.不带时区跳变的,会出现时间重 ...

  3. Java虚拟机运行时数据区

    运行时数据区程序计数器Java虚拟机栈本地方法栈Java堆(GC堆)方法区运行时常量池 运行时数据区 Java虚拟机在运行Java程序时,会将它所管理的内存划分为若干个内存区域.这些数据区域有各自的用 ...

  4. Android 如何去掉手机中横竖屏切换时的转屏动画?

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  5. 让多个Fragment 切换时不重新实例化

    转自:http://www.yrom.net/blog/2013/03/10/fragment-switch-not-restart/ 让多个Fragment 切换时不重新实例化 在项目中需要进行Fr ...

  6. 关于Java虚拟机运行时数据区域的总结

    Java虚拟机运行时数据区域 程序计数器(Program Counter) 程序计数器作为一个概念模型,这个是用来指示下一条需要执行的字节码指令在哪. Java的多线程实际上是通过线程轮转做到的,如果 ...

  7. Java时区问题

    Java时区相关 时间格式 UTC是以原子时计时,更加精准,适应现代社会的精确计时.不过一般使用不需要精确到秒时,视为等同.GMT是前世界标准时,UTC是现世界标准时.每年格林尼治天文台会发调时信息, ...

  8. 面试常问的 Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

  9. Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

随机推荐

  1. Tutorial1

    一 Introduction to tf2 本部分是关于tf2简单介绍,比如tf2能做什么,并使用一个turtlesim的例子来显示tf2在多机器人中的一些能力.同时也包括一些工具的使用,比如tf2_ ...

  2. go语言学习之从例子开始

    [目录] go语言从例子开始之Example1.helloworld go语言从例子开始之Example2.类型 go语言从例子开始之Example3.变量 go语言从例子开始之Example4.常量 ...

  3. rabbit 独占队列

    std::string queue_name = "hello"; AmqpClient::Channel::ptr_t channel = AmqpClient::Channel ...

  4. 第03章 AOP前奏

    第03章 AOP前奏 提出问题 ●情景:数学计算器 ●要求 ①执行加减乘除运算 ②日志:在程序执行期间追踪正在发生的活动 ③验证:希望计算器只能处理正数的运算 ●常规实现 ●问题 ○代码混乱:越来越多 ...

  5. 190行代码实现mvvm模式

    前言 网上讲 vue 原理,mvvm 模式的实现,数据双向绑定的文章一搜一大堆,不管写的谁好谁坏,都是写的自己的理解,我也发一篇文章记录自己的理解,如果对看官有帮助,那也是我莫大的荣幸,不过看完之后, ...

  6. Java集合框架Map接口

    集合框架Map接口 Map接口: 键值对存储一组对象 key不能重复(唯一),value可以重复 常用具体实现类:HashMap.LinkedHashMap.TreeMap.Hashtable Has ...

  7. kd树解平面最近点对

    早上起来头有点疼,突然就想到能不能用kd树解平面最近点对问题,就找了道题试了一下,结果可以,虽然效率不高,但还是AC了~ 题目链接:http://acm.hdu.edu.cn/showproblem. ...

  8. java中switch的用法以及判断的类型有哪些(String\byte\short\int\char\枚举类型)

    switch关键字对于多数java学习者来说并不陌生,由于笔试和面试经常会问到它的用法,这里做了一个简单的总结: 能用于switch判断的类型有:byte.short.int.char(JDK1.6) ...

  9. Java基本数据类型及所占字节大小

    一.Java基本数据类型 基本数据类型有8种:byte.short.int.long.float.double.boolean.char 分为4类:整数型.浮点型.布尔型.字符型. 整数型:byte. ...

  10. JS中Object的一些关于原型的方法

    1.Object.getPrototypeOf(obj) 该方法返回 obj 对象的原型对象,等同于 obj.__proto__.获取对象的原型对象推荐使用该方法而不是 obj.__proto__方法 ...