java 时区问题

https://www.iteye.com/blog/moon-walker-2396035

在国际化的项目中需要处理的日期时间问题主要有两点:

1、日期时间的国际化格式问题处理;

2、日期时间的时区问题处理,这两个问题要区分开,不要弄混了。

1、日期时间国际化化格式处理

对应的关键词:Locale

日期时间的国际化格式指的是在不同的国家和地区对日期时间的显示方式不同,主要通过不同国家地区不同的语言习惯,对同一个实现的呈现方式不同。在java中需要结合Locale类进行处理:

Java代码

public static void main(String[] args) {  
        Date date = new Date();  
        Locale locale = Locale.CHINA;  
        DateFormat shortDf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM, locale);  
        System.out.println("中国格式:"+shortDf.format(date));  
   
        locale = Locale.ENGLISH;  
        shortDf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM, locale);  
        System.out.println("英国格式:"+shortDf.format(date));  
}  

执行mian方法,结果为:

Java代码 收藏代码

中国格式:2017-10-12 10:29:44  
英国格式:Oct 12, 2017 10:29:44 AM  

在Spring MVC项目中,一般可以借助spring自动的国际化解决方案,在视图层对不同的国家使用不同的locale参数进行处理。

2、日期时间国际化化时区处理

对应的关键词:TimeZone

日期时间的时区问题,指的是在同一时刻,地球上的各个地区的日期时间不同。全球划分为24个时区,每个相邻时区时间相差一个小时(中国为了方便统一,虽然跨越5个时区,但都使用同一个时区时间),也就是说在同一时刻,全球同一时刻对应的当地时间的小时数有可能是0-23点之间的一个值。这里拿中国上海和英国伦敦举例:

Java代码

public static void main(String[] args) {  
        Date date = new Date();  
        Locale locale = Locale.CHINA;  
        DateFormat shortDf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM, locale);  
        shortDf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));//Asia/Chongqing  
        System.out.println(TimeZone.getDefault().getID());  
        System.out.println("中国当前日期时间:" + shortDf.format(date));  
   
        locale = Locale.ENGLISH;  
        shortDf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM, locale);  
        shortDf.setTimeZone(TimeZone.getTimeZone("Europe/London"));  
        System.out.println("英国当前日期时间:"+shortDf.format(date));  
    }  
   

执行main方法,运行结果为:

Java代码 收藏代码

中国当前日期时间:2017-10-12 10:55:55  
英国当前日期时间:Oct 12, 2017 3:55:55 AM  

说明同一时刻,中国上海和英国伦敦相差7个小时,也就是相差7个时区。

时区对国际化项目带来的问题

日期时间的国际化格式处理 只是显示风格问题 相对来说比较简单,但日期时间的国际化时区问题 确比较麻烦,如果处理不当会引起一些兼容性问题。

拿最近做的一个泰国项目举例,我们一个活动页创建项目部署在泰国。如果在中国创建一个活动页,通过时间控件选择活动的开始时间,这时获取的时间是从浏览器获取 为中国时区时间。需要把这个时间传到后端服务器,存储到数据库,但服务器的时间为泰国时区的时间。中国是东八区 泰国是东七区,相差一个小时。这时有两种处理办法:

1、前端传给后端的是字符串,比如开始时间为“2017-10-12 08:00:00”, 后端直接使用这个字符串转换为泰国的Date 存入数据库即可。

Java代码

public static void main(String[] args) throws Exception{  
        String t="2017-10-12 08:00:00";  
        DateFormat format =  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        Date date = format.parse(t);  
        System.out.println(date);  
    }  
   

看起来比较简单,但关键是这个字符串,前面说了 不同的语言国家这个字符的格式不同,后端需要根据不同的格式进行Format操作。假设换成在英国创建活动,这个Format又得改成英国的格式。

2、前端传给后端的是时间戳,比如开始时间为“2017-10-12 08:00:00”,对应的中国的时间戳为:1507766400000,转换成泰国的时间就变成:“2017-10-12 07:00:00”,模拟代码如下:
Java代码

public static void main(String[] args) throws Exception{  
        String t="2017-10-12 08:00:00";//页面传入的时间  
        DateFormat format =  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        Date date = format.parse(t);  
        System.out.println("中国:"+t);  
   
   
        long tl = date.getTime();//模拟前端转换为中国的时间戳  
        TimeZone timeZone2 = TimeZone.getTimeZone("Asia/Bangkok");  
        format.setTimeZone(timeZone2);  
        String d2 = format.format(tl);  
        System.out.println("泰国:"+d2);//存入数据库的时间  
}  

执行结果为:

中国:2017-10-12 08:00:00  
泰国:2017-10-12 07:00:00  

说明直接传给后端时间搓有问题,但有解决办法:首先后端封装一个接口后获取服务器相对GMT(格林尼治标准时间)时间的偏移量:

Java代码 收藏代码
TimeZone zone = TimeZone.getDefault();
System.out.println(zone.getRawOffset());
这段代码放在不同时区的服务器上执行结果会不同(前提是服务器的时区设置跟本地时区一致)。如果在泰国执行结果为25200000ms,换算成小时为7,说明泰国的时区的偏移量相对于GMT标准时间相差7小时。下文简称“时区偏移量”。

以下操作都在浏览器中通过js代码实现:

前端首先调用该接口获取服务器的时区偏移量,再在浏览器上获取本地的时区偏移量,计算出两个偏移量的差值。本地浏览器上获取当前的时间戳,减去上一步计算出来的差值即可得到服务器这个时间的时间戳,把这个时间戳传给后端 再转换成时间,就是服务器对应的时间,存入数据库即可。

Js代码 收藏代码

//服务的时区偏移量,通过接口获得,注意换成负值  
var serveroffset=-25200000;  
var d = new Date();  
//获取本地浏览器的时区偏移量  
var localOffset = d.getTimezoneOffset() * 60000;  
//的到本地和偏移量的差值  
var deffoffset=localOffset-(serveroffset);  
//获取本地浏览器时间戳  
var localTime = d.getTime();  
//计算出传到服务器的时间戳  
var servertime=localTime+deffoffset;  

通过上述方式,可以实现服务器全球各地部署,系统都可以正常使用。

Java中的TimeZone类

Java中处理时区使用的是TimeZone类,通过TimeZone.getTimeZone(String id)方法可以获取到指定时区的TimeZone实例,通过TimeZone实例可以获取到相对于GMT标准时间的偏移量。该方法的参数ID可以是GMT、 UTC、CST等时区,也可以是城市名:

Java代码 收藏代码

public static void main(String[] args) throws Exception{  
   
        TimeZone timeZone1 = TimeZone.getDefault();//获取当前服务器时区  
        TimeZone timeZone2 = TimeZone.getTimeZone("Asia/Shanghai");//获取上海时区  
        TimeZone timeZone3 = TimeZone.getTimeZone("GMT");//获取格林威治标准时区  
        TimeZone timeZone4 = TimeZone.getTimeZone("GMT+8");//获取东八区时区  
        TimeZone timeZone5 = TimeZone.getTimeZone("UTC");//获取UTC标准时间  
        TimeZone timeZone6 = TimeZone.getTimeZone("CST");//获取CST时区  
   
        System.out.println(timeZone1.getRawOffset());  
        System.out.println(timeZone2.getRawOffset());  
        System.out.println(timeZone3.getRawOffset());  
        System.out.println(timeZone4.getRawOffset());  
        System.out.println(timeZone5.getRawOffset());  
        System.out.println(timeZone6.getRawOffset());  
    }  
   

运行结果:

28800000  
28800000  
0  
28800000  
0  
-21600000  

GMT和 UTC可以视为几乎是等同的,UTC更精准,有闰秒的概念。

其中TimeZone.getTimeZone("Asia/Shanghai")和TimeZone.getTimeZone("GMT+8")是相同的,可以相互替换使用。又比如泰国的时区ID使用"Asia/Bangkok"和"GMT+7"是相同。

posted @ 2021-03-26 13:44  zhanglw  阅读(800)  评论(0)    收藏  举报