8.3.5 将GMT转换成本地时间
对于国际化的程序,往往有如下的需求:
在某一时区A的用户向系统提交的数据被保存在了服务端的数据库中。同时,系统也保存了时区A的用户提交请求时的GMT。而在另一个时区B的用户要浏览时区A的用户录入的信息。但时间要显示成时区B的本地时间。
对于上面的需求,最简单的方法是将服务端保存的GMT直接转换成时区B的本地时间。对于Web系统。这个工作可以交给JavaScript来完成。也就是说,服务端将服务端保存的日期/时间的相应值(年、月、日、时、分、秒)传给客户端,然后JavaScript再根据本地的时区将其以本地时间输出(JavaScript会考虑到夏令时)。
JavaScript的Date类可以很容易地完成这个工作。通过Date类的setUTCXxx方法可以设置年、月、日、时、分、秒信息。但要注意所设置的时间是UTC(GMT)时间,不是本地时间。因此,服务端传过来的必须是UTC(GMT)时间。设置年、月、日、时、分、秒的setUTCXxx方法如下:
l setUTCFullYear:设置年(4位)。
2 setUTCMonth:设置月,从0开始,也就是说,0表示1月、1表示2月,以此类推。
3 setUTCDate:设置日。
4 setUTCHours:设置小时。
5 setUTCMinutes:设置分。
6 setUTCSeconds:设置秒。
除此之外,JavaScript还可以判断操作系统的当前时区在指定时间是否处于夏令时。Date类有一个getTimezoneOffset方法,该方法返回本地时间和GMT的时间差(单位:分钟)。如果当前时区的指定时间不是夏令时,那么将Date对象设为一年中的任何一天,调用getTimezoneOffset方法返回的值都是相同的。如果采用了夏令时,在夏令时的时间和非夏令时的时间使用getTimezoneOffset方法返回的值差了60分钟(1小时),因此,只需要为Date对象指定相应的时间,并判断两个不同时间的Date对象的getTimezoneOffset方法的返回值是否相等就可以得知当前时区的指定时间是否处于夏令时。
由于每个国家或地区的夏令时的开始和结束时间不同,但1月份都会处在非夏令时的时间段,而7月份都会处在夏令时的时间段。因此,可以选中1月1号和7月1号来判断这两天的时差是否相同。
下面的JavaScript代码判断了当前时区指定的时间是否采用了夏令时:
var date = new Date();
date.setUTCFullYear(2002);
date.setUTCMonth(6); // 设置7月
date.setUTCDate(1); // 设置1日
var offset1 = date.getTimezoneOffset(); // 返回2002年夏令时和GMT的时间差
date.setUTCMonth(0); // 设置1月
var offset2 = d.getTimezoneOffset(); // 返回2002年非夏令时和GMT的时间差
// 如果offset1和offset2不相等,表示2002年采用了夏令时
if(offset1 != offset2)
{
alert("夏令时");
}
为了使用JavaScript向服务端发送请求方便,在本例中使用了prototype组件。该组件只是一个JavaScript文件(prototype.js)。该文件可以从如下的网址下载:
http://www.prototypejs.org/download
在我写作本书时,prototype.js的最新版本是1.6,本书使用的也是该版本。
在<Web根目录>建立一个javascript目录,将prototype.js文件放到该目录即可。
例子 : 在Web程序中将GMT转换成本地时间
1. 实例说明
本实例通过Servlet向客户端发送信息。在发送的信息中包含了一个GMT时间的年、月、日、时、分、秒信息。客户端的JSP页面通过prototype组件以同步的方式请求该Servlet,并获得该Servlet返回的信息。然后通过Date对象将Servlet发送的客户端的GMT时间信息转换成本地时间,并显示在页面中。
2. 编写GMTServlet
GMTServlet是一个Servlet类,负责向客户端发送GMT时间信息。该类的实现代码如下:
package chapter8.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GMTServlet extends HttpServlet
{
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
Calendar calendar = Calendar.getInstance();
// GMT:2008年10月21日 21时16分34秒
calendar.set(2008, 9, 21, 21, 16, 34);
// 设置要发送到客户端的年、月、日、时、分、秒
out.println("{'year':" + calendar.get(Calendar.YEAR) + ",");
out.println("'month':" + calendar.get(Calendar.MONTH) + ",");
out.println("'date':" + calendar.get(Calendar.DATE) + ",");
out.println("'hour':" + calendar.get(Calendar.HOUR_OF_DAY) + ",");
out.println("'minute':" + calendar.get(Calendar.MINUTE) + ",");
out.println("'second':" + calendar.get(Calendar.SECOND) + "}");
}
}
在上面的代码中使用“2008年10月21日 21时16分34秒”作为一个GMT。读者也可以通过其他方式指定GMT,如从数据库中读取。如果读者想将本地时间转换成GMT再保存,可以使用8.3.4节中介绍的方法。
由于客户端使用prototype组件发送、接收请求,并将响应信息转换成JavaScript对象。因此,GMTServlet返回的信息必须符合prototype组件要求的格式(prototype组件采用了json格式标准)。如果只将返回信息映射成简单对象,如obj.name形式。只需要使用如下的格式即可:
{"property1":value1, "property2": value2, ... ,}
prototype组件会将符合上面格式的内容转换成JavaScript对象。冒号(:)前面的部分将被作为对象的属性名,后面的部分将作为属性值。如可以通过如下的代码访问该对象的属性:
alert(obj.property1);
根据上面的格式,GMTServlet程序返回的信息应是如下的格式:
{'year': 2008,
'month': 9,
'date': 21,
'hour': 21,
'minute': 16,
'second': 34}
prototype组件在将上面的内容转换成JavaScript对象后,可通过如下的代码访问相关的属性:
alert(obj.year);
alert(obj.month);
下面是GMTServlet类的配置代码:
3. 编写gmt.jsp
gmt.jsp页面负责通过prototype组件请求GMTServlet,并将GMTServlet返回的GMT时间转换成本地时间,最后将本地时间显示在页面中。gmt.jsp页面的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<!-- 引用prototype组件 -->
<script type="text/javascript" src="../javascript/prototype.js">
</script>
</head>
<body>
<!-- 用于显示本地时间 -->
<div id="div"></div>
<script type="text/javascript">
function requestGMTServlet()
{
// 要请求的url
var url = "GMTServlet";
// 以同步的方式请求GMTServlet
var myAjax = new Ajax.Request(url, {
method :'post',
parameters :'',
onComplete :processGMT,
asynchronous :false
});
}
// 如果GMTServlet成功返回GMT信息,则回调processGMT方法
function processGMT(request)
{
// 将GMTServlet返回的GMT信息转换成JavaScript对象
var obj = request.responseText.evalJSON();
var date = new Date();
// 使用obj对象的属性值设置date对象
date.setUTCFullYear(obj.year);
date.setUTCMonth(obj.month);
date.setUTCDate(obj.date);
date.setUTCHours(obj.hour);
date.setUTCMinutes(obj.minute);
date.setUTCSeconds(obj.second);
// 创建用于判断当前时区的指定时间是否处于夏令时的Date对象
var date1 = new Date();
// 设置date1对象为7月1日
date1.setUTCFullYear(obj.year);
date1.setUTCMonth(6);
date1.setUTCDate(1);
// 返回夏令时和GMT的时差
var offset1 = date1.getTimezoneOffset();
// 设置date1对象为1月
date1.setUTCMonth(0);
// 返回非夏令时和GMT的时差
var offset2 = date1.getTimezoneOffset();
// 获得div标签对象
var div = document.getElementById("div");
// 如果offset1和offset2不相等,则表示当前时区的指定时间处于夏令时
if (offset1 != offset2)
{
div.innerHTML += "夏令时<br>";
}
div.innerHTML += "本地时间:" + date.toLocaleString();
}
requestGMTServlet();
</script>
</body>
</html>
4. 测试非夏令时的情况
读者可以将操作系统的时区设为非夏令时的时区,如GMT+8(北京时间)。在浏览器地址栏中输入如下的URL:
http://localhost:8080/demo/chapter8/gmt.jsp
浏览器中显示的信息如图8.17所示。
图8.17 显示非夏令时的时区的本地时间
由于GMT+8时区的本地时间林GMT快了8小时,因此,GMTServlet类中的GMT对应的GMT+8的时间就是“2008年10月22日 5:16:34”。
5. 测试夏令时的情况
在这一步读者需要将操作系统的时区修改成使用夏令时的时区。如“伦敦”所处的GMT时区。并且将当前月份设为4至10月之间。并刷新图8.17所示的页面。这时浏览器中将显示如图8.18所示的信息。
图8.18 显示夏令时的时区的本地时间
小结
本章主要介绍了Java对国际化的支持。国际化的核心思想就是按着不同国家或地区的文化习惯显示相应的信息,如日期/时间、货币等。除此之外,就是根据本地信息显示不同国家的文字信息了。一般将这些文字信息保存在资源文件(.properties文件)中,当然,也可以将其保存在资源类中。
Web系统中的国际化思想和其他类型的程序一样,也需要使用资源文件来保存国际化信息。只是封装本地信息的Locale对象需要通过分析HTTP请求消息头的Accept-Language字段来获得。
GMT是世界标准时间,也可以将GMT看作是时间中的世界语。所有其他的本地时间都以GMT作为参考。UTC要比GMT更精确一些,但在大多数时将GMT和UTC视为同一个时间。通过TimeZone类的一系列方法可以将GMT(UTC)和本地时间互相转换。
从图8.18所示的效果可以看出,由于当前时区处于夏令时,因此,在页面中显示出了“夏令时”信息。而且由于夏令时要快一个小时,因此,当前伦敦的时间应该比GMT标准时间快了1个小时,所有是22点。