热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat

这篇文章主要介绍了Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat的区别于用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、概述

1、问题描述

使用Java处理时间时,我们可能会经常发现时间不对,比如相差8个小时等等,其真实原因便是TimeZone。只有正确合理的运用TimeZone,才能保证系统时间无论何时都是准确的。由于我在外企工作,服务器在美国,美国也有很多时区,经常会碰到向处于不同时区的服务器发请求时需要考虑时区转换的问题。譬如,服务器位于西八区(GMT-8:00),而身处东八区的用户想要查询当天的销售记录。则需把东八区的“今天”这个时间范围转换为服务器所在时区的时间范围。

2、时区认识

GMT时间:即格林威治平时(Greenwich Mean Time)。平太阳时是与视太阳时对应的,由于地球轨道非圆形,运行速度随地球与太阳距离改变而出现变化,因此视太阳时欠缺均匀性。为了纠正这种不均匀 性,天文学家就计算地球非圆形轨迹与极轴倾斜对视太阳时的效应,而平太阳时就是指经修订之后的视太阳时。在格林威治子午线上的平太阳时称为世界时(UTC), 又叫格林威治平时(GMT)。

3、Java 时间和时区API

3.1、Date

类Date表示特定的瞬间,精确到毫秒。获得一个表示当前时间的Date对象有两种方式:

Date date = new Date(); 
Date date = Calendar.getInstance().getTime(); 

Date对象本身所存储的毫秒数可以通过date.getTime()方法得到;该函数返回自1970年1月1日 00:00:00 GMT以来此对象表示的毫秒数。它与时区和地域没有关系(其实可以认为是GMT时间),而且还会告诉我们这个时区是否使用夏令时。有个这个信息,我们就能够继续将时区对象和日期格式化器结合在一起在其它的时区和其它的语言显示时间了。

3.2、 Calendar

Calendar的getInstance()方法有参数为TimeZone和Locale的重载,可以使用指定时区和语言环境获得一个日历。无参则使用默认时区和语言环境获得日历。

3.3、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。

4、影响TimeZone的因素

1. 操作系统的时区设置。

2. 数据传输时时区设置。

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

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

5、解决的方法:

从以上的分析可以看出,解决时区问题就简单了,在时区间转换时间时,首先用原时间减掉原时间所在时区相对于GMT的偏移量,得到原时间相对于GMT的值,然后再加上目标时区相对GMT的偏移量即可。需要注意的是这样得到的结果依然是毫秒数,所以我们要按照指定日期格式重新转换成Date对象即可。

6、实例:

在实例之前,假设当前的时区为中国的东八区。即GMT+8:00

package com.wsheng.aggregator.timezone;

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email swang6@ebay.com
 * 
 */
public class TimeZone1 {
	
	public static void main(String[] args) {
	 Date date = new Date(1391174450000L); // 2014-1-31 21:20:50 
	 String dateStr = "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(dateStr); 
	  System.out.println(dateTmp); 
	  } catch (ParseException e) { 
	  e.printStackTrace(); 
	 } 
	 String dateStrTmp = dateFormat.format(date); 
	 System.out.println(dateStrTmp); 
	}
	

}

执行结果:

Sat Feb 01 05:20:50 CST 2014
2014-01-31 13:20:50

我们发现同一时间,字符串和日期运行出来的结果并不相同,那么我们应该怎么理解呢?

一切都要以根本原因, 即当前操作系统的时间为基准。

我的操作系统 是"Asia/Shanghai",即GMT+8的北京时间,那么执行日期转字符串的format方法时,由于日期生成时默认是操作系统时区,因此 2014-1-31 21:20:50是北京时间,那么推算到GMT时区,自然是要减8个小时的,即结果(2014-01-31 13:20:50);而执行字符串转日期的parse方法时,由于字符串本身没有时区的概念,因此 2013-1-31 22:17:14就是指GMT(UTC)时间【ps:所有字符串都看做是GMT时间】,那么当转化为日期时要加上默认时区, 即"Asia/Shanghai",因此要加上8个小时。

用Calendar的话,如下:

package com.wsheng.aggregator.timezone;

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email swang6@ebay.com
 * 
 */
public class TimeZone2 {
	
 public static void main(String[] args) { 
 	Date date = new Date(1391174450000L); // 2014-1-31 21:20:50 
  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)); 
 } 

}

执行结果:

Fri Jan 31 21:20:50 CST 2014
13:20

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

正确的做法如下:

package com.wsheng.aggregator.timezone;

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email swang6@ebay.com
 * 
 */
public class TimeZone3 {
	
 public static void main(String[] args) { 
 	Date date = new Date(1391174450000L); // 2014-1-31 21:20:50 
  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()); 
 } 

}

执行结果:

Fri Jan 31 21:20:50 CST 2014
Fri Jan 31 13:20:50 CST 2014

完美通用转换方法

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

/**
 * 
 */
package com.wsheng.aggregator.timezone;

import java.util.Date;
import java.util.TimeZone;

/**
 * @author Josh Wang(Sheng)
 * 
 * @email swang6@ebay.com
 * 
 */
public class TimeZone4 {
	
 public static void main(String[] args) { 
 	Date date = new Date(1391174450000L); // 2014-1-31 21:20:50 
  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; 
 } 

}

运行结果:

Fri Jan 31 21:20:50 CST 2014
Fri Jan 31 13:20:50 CST 2014

更通用的,我们可以写一个支持类型转换的类:

package com.wsheng.aggregator.timezone;
import java.text.*; 
import java.util.*; 
 
/**
 * 
 * @author Josh Wang(Sheng)
 * 
 * @email swang6@ebay.com
 *
 */
public class DateTransformer { 
 public static final String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss"; 
   
 public static String dateTransformBetweenTimeZone(Date sourceDate, DateFormat formatter, 
  TimeZone sourceTimeZone, TimeZone targetTimeZone) { 
  Long targetTime = sourceDate.getTime() - sourceTimeZone.getRawOffset() + targetTimeZone.getRawOffset(); 
  return DateTransformer.getTime(new Date(targetTime), formatter); 
 } 
   
 public static String getTime(Date date, DateFormat formatter){ 
  return formatter.format(date); 
 } 
   
 public static void main(String[] args){ 
  DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); 
  Date date = Calendar.getInstance().getTime(); 
  System.out.println(" date: " + date);
  
  TimeZone srcTimeZOne= TimeZone.getTimeZone("EST"); 
  TimeZone destTimeZOne= TimeZone.getTimeZone("GMT+8"); 
  System.out.println(DateTransformer.dateTransformBetweenTimeZone(date, formatter, srcTimeZone, destTimeZone)); 
 } 
} 

DateFormat是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年 1 月 1 日 00:00:00 这一刻开始的毫秒数。SimpleDateFormat则是一个以与语言环境有关的方式来格式化和解析日期的具体类,可以以“日期和时间模式”字符串指定日期和时间格式。我们函数中所用模式字符串为"MM/dd/yyyy HH:mm:ss",则输出日期:"07/16/2013 04:00:00"

其他常见的模式字母定义如下:

字母  日期或时间元素  表示  示例
G Era 标志符 Text AD
y   年 Year 1996; 96 
M 年中的月份 Month July; Jul; 07 
w 年中的周数 Number 27
W 月份中的周数 Number 2
D 年中的天数 Number 189
d 月份中的天数 Number 10
F 月份中的星期 Number 2
E 星期中的天数 Text Tuesday; Tue 
a Am/pm 标记 Text PM
H 一天中的小时数(0-23) Number 0
k 一天中的小时数(1-24) Number 24
K am/pm 中的小时数(0-11) Number 0
h am/pm 中的小时数(1-12) Number 12
m 小时中的分钟数 Number 30
s 分钟中的秒数 Number 55
S 毫秒数 Number 978
z 时区 General time zone Pacific Standard Time; PST; GMT-08:00 
Z 时区 RFC 822 time zone -0800

由上面的分析和事例说明可知:

1. 计算机内部记录的时间(Date date = new Date()), 为格林威治标准时(GMT). 即java.util.Date代表一个时间点,其值为距公元1970年1月1日 00:00:00的毫秒数。所以它可以认为是没有时区和Locale概念的。

2. 日期格式化类DateFormat, 对于不同地区的配置一般有两个点, 一个是Locale , 一个是TimeZone
     前者(Locale)使DateFormat按所配置的地区特性来输出文字(例如中国,美国,法国不同地区对日期的表示格式不一样,中国可能是2001年10月5日)
     后者(TimeZone)让DateFormat知道怎么去转换,去调整时间偏移度,从而得到符合配置的时区的时间.
(即假设取得当前时间(假设当前时区为GMT+0,即与new Date()最后转换的时间毫秒数一致)为2:00, 那么如果你配置DateFormat.setTimeZome("GMT+8"), 即北京时间的时区, 那么这时候格式化输出的就是10:00了, 因为系统对原始毫秒数进行了时间偏移调整(调到你设置的时区),即加多8小时,之后再格式化输出日期的字符串形式)

3. GMT与UTC的时区是一样的,都是以伦敦时间为基准. 而GMT+8时区就是北京时间所在时区.同一时刻的时间比GMT快8小时。

到此这篇关于Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat的区别于用法的文章就介绍到这了,更多相关Java时区处理 Date,Calendar,TimeZone,SimpleDateFormat内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何基于ggplot2构建相关系数矩阵热图以及一个友情故事
    本文介绍了如何在rstudio中安装ggplot2,并使用ggplot2构建相关系数矩阵热图。同时,通过一个友情故事,讲述了真爱难觅的故事背后的数据量化和皮尔逊相关系数的概念。故事中的小伙伴们在本科时参加各种考试,其中有些沉迷网络游戏,有些热爱体育,通过他们的故事,展示了不同兴趣和特长对学习和成绩的影响。 ... [详细]
author-avatar
我并没有你们想象P的坚强
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有