java国际化之时区问题处理

原文:https://moon-walker.iteye.com/blog/2396035

 

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

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

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

 

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

 

对应的关键词:Locale

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

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

 

 执行mian方法,结果为:

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

 

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

 

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

对应的关键词: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.FULL,DateFormat.FULL, 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方法,运行结果为:

  1. 中国当前日期时间:2017-10-12 10:55:55  
  2. 英国当前日期时间: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);//存入数据库的时间  
}  

执行结果为:

  1. 中国:2017-10-12 08:00:00  
  2. 泰国: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());  
    }  

 运行结果:

  1. 28800000  
  2. 28800000  
  3. 0  
  4. 28800000  
  5. 0  
  6. -21600000 

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

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

 

通过阅读jdk源码可以发现,TimeZone ID是在java里ZoneInfoFile类加载的。在jvm初始化的时候,会读取jdk安装目录下的 ${ java.home } /jre/lib/tzdb.dat,放到其成员变量为zones的ConcurrentHashMap里。当调用TimeZone.getTimeZone(id)方法时,会用id到这个map里进行匹配获取到指定id的时区。

 

最后附上TimeZone 的ID列表,要获取对应城市的时区,需要先查询到其对应的ID,对应的其实就是 ${ java.home } /jre/lib/tzdb.dat文件中的内容。

 
Etc/GMT+12  
Etc/GMT+11  
MIT  
Pacific/Apia  
Pacific/Midway  
Pacific/Niue  
Pacific/Pago_Pago  
Pacific/Samoa  
US/Samoa  
America/Adak  
America/Atka  
Etc/GMT+10  
HST  
Pacific/Fakaofo  
Pacific/Honolulu  
Pacific/Johnston  
Pacific/Rarotonga  
Pacific/Tahiti  
SystemV/HST10  
US/Aleutian  
US/Hawaii  
Pacific/Marquesas  
AST  
America/Anchorage  
America/Juneau  
America/Nome  
America/Yakutat  
Etc/GMT+9  
Pacific/Gambier  
SystemV/YST9  
SystemV/YST9YDT  
US/Alaska  
America/Dawson  
America/Ensenada  
America/Los_Angeles  
America/Tijuana  
America/Vancouver  
America/Whitehorse  
Canada/Pacific  
Canada/Yukon  
Etc/GMT+8  
Mexico/BajaNorte  
PST  
PST8PDT  
Pacific/Pitcairn  
SystemV/PST8  
SystemV/PST8PDT  
US/Pacific  
US/Pacific-New  
America/Boise  
America/Cambridge_Bay  
America/Chihuahua  
America/Dawson_Creek  
America/Denver  
America/Edmonton  
America/Hermosillo  
America/Inuvik  
America/Mazatlan  
America/Phoenix  
America/Shiprock  
America/Yellowknife  
Canada/Mountain  
Etc/GMT+7  
MST  
MST7MDT  
Mexico/BajaSur  
Navajo  
PNT  
SystemV/MST7  
SystemV/MST7MDT  
US/Arizona  
US/Mountain  
America/Belize  
America/Cancun  
America/Chicago  
America/Costa_Rica  
America/El_Salvador  
America/Guatemala  
America/Indiana/Knox  
America/Indiana/Tell_City  
America/Knox_IN  
America/Managua  
America/Menominee  
America/Merida  
America/Mexico_City  
America/Monterrey  
America/North_Dakota/Center  
America/North_Dakota/New_Salem  
America/Rainy_River  
America/Rankin_Inlet  
America/Regina  
America/Swift_Current  
America/Tegucigalpa  
America/Winnipeg  
CST  
CST6CDT  
Canada/Central  
Canada/East-Saskatchewan  
Canada/Saskatchewan  
Chile/EasterIsland  
Etc/GMT+6  
Mexico/General  
Pacific/Easter  
Pacific/Galapagos  
SystemV/CST6  
SystemV/CST6CDT  
US/Central  
US/Indiana-Starke  
America/Atikokan  
America/Bogota  
America/Cayman  
America/Coral_Harbour  
America/Detroit  
America/Fort_Wayne  
America/Grand_Turk  
America/Guayaquil  
America/Havana  
America/Indiana/Indianapolis  
America/Indiana/Marengo  
America/Indiana/Petersburg  
America/Indiana/Vevay  
America/Indiana/Vincennes  
America/Indiana/Winamac  
America/Indianapolis  
America/Iqaluit  
America/Jamaica  
America/Kentucky/Louisville  
America/Kentucky/Monticello  
America/Lima  
America/Louisville  
America/Montreal  
America/Nassau  
America/New_York  
America/Nipigon  
America/Panama  
America/Pangnirtung  
America/Port-au-Prince  
America/Resolute  
America/Thunder_Bay  
America/Toronto  
Canada/Eastern  
Cuba  
EST  
EST5EDT  
Etc/GMT+5  
IET  
Jamaica  
SystemV/EST5  
SystemV/EST5EDT  
US/East-Indiana  
US/Eastern  
US/Michigan  
America/Caracas  
America/Anguilla  
America/Antigua  
America/Aruba  
America/Asuncion  
America/Barbados  
America/Blanc-Sablon  
America/Boa_Vista  
America/Campo_Grande  
America/Cuiaba  
America/Curacao  
America/Dominica  
America/Eirunepe  
America/Glace_Bay  
America/Goose_Bay  
America/Grenada  
America/Guadeloupe  
America/Guyana  
America/Halifax  
America/La_Paz  
America/Manaus  
America/Marigot  
America/Martinique  
America/Moncton  
America/Montserrat  
America/Port_of_Spain  
America/Porto_Acre  
America/Porto_Velho  
America/Puerto_Rico  
America/Rio_Branco  
America/Santiago  
America/Santo_Domingo  
America/St_Barthelemy  
America/St_Kitts  
America/St_Lucia  
America/St_Thomas  
America/St_Vincent  
America/Thule  
America/Tortola  
America/Virgin  
Antarctica/Palmer  
Atlantic/Bermuda  
Atlantic/Stanley  
Brazil/Acre  
Brazil/West  
Canada/Atlantic  
Chile/Continental  
Etc/GMT+4  
PRT  
SystemV/AST4  
SystemV/AST4ADT  
America/St_Johns  
CNT  
Canada/Newfoundland  
AGT  
America/Araguaina  
America/Argentina/Buenos_Aires  
America/Argentina/Catamarca  
America/Argentina/ComodRivadavia  
America/Argentina/Cordoba  
America/Argentina/Jujuy  
America/Argentina/La_Rioja  
America/Argentina/Mendoza  
America/Argentina/Rio_Gallegos  
America/Argentina/Salta  
America/Argentina/San_Juan  
America/Argentina/San_Luis  
America/Argentina/Tucuman  
America/Argentina/Ushuaia  
America/Bahia  
America/Belem  
America/Buenos_Aires  
America/Catamarca  
America/Cayenne  
America/Cordoba  
America/Fortaleza  
America/Godthab  
America/Jujuy  
America/Maceio  
America/Mendoza  
America/Miquelon  
America/Montevideo  
America/Paramaribo  
America/Recife  
America/Rosario  
America/Santarem  
America/Sao_Paulo  
Antarctica/Rothera  
BET  
Brazil/East  
Etc/GMT+3  
America/Noronha  
Atlantic/South_Georgia  
Brazil/DeNoronha  
Etc/GMT+2  
America/Scoresbysund  
Atlantic/Azores  
Atlantic/Cape_Verde  
Etc/GMT+1  
Africa/Abidjan  
Africa/Accra  
Africa/Bamako  
Africa/Banjul  
Africa/Bissau  
Africa/Casablanca  
Africa/Conakry  
Africa/Dakar  
Africa/El_Aaiun  
Africa/Freetown  
Africa/Lome  
Africa/Monrovia  
Africa/Nouakchott  
Africa/Ouagadougou  
Africa/Sao_Tome  
Africa/Timbuktu  
America/Danmarkshavn  
Atlantic/Canary  
Atlantic/Faeroe  
Atlantic/Faroe  
Atlantic/Madeira  
Atlantic/Reykjavik  
Atlantic/St_Helena  
Eire  
Etc/GMT  
Etc/GMT+0  
Etc/GMT-0  
Etc/GMT0  
Etc/Greenwich  
Etc/UCT  
Etc/UTC  
Etc/Universal  
Etc/Zulu  
Europe/Belfast  
Europe/Dublin  
Europe/Guernsey  
Europe/Isle_of_Man  
Europe/Jersey  
Europe/Lisbon  
Europe/London  
GB  
GB-Eire  
GMT  
GMT0  
Greenwich  
Iceland  
Portugal  
UCT  
UTC  
Universal  
WET  
Zulu  
Africa/Algiers  
Africa/Bangui  
Africa/Brazzaville  
Africa/Ceuta  
Africa/Douala  
Africa/Kinshasa  
Africa/Lagos  
Africa/Libreville  
Africa/Luanda  
Africa/Malabo  
Africa/Ndjamena  
Africa/Niamey  
Africa/Porto-Novo  
Africa/Tunis  
Africa/Windhoek  
Arctic/Longyearbyen  
Atlantic/Jan_Mayen  
CET  
ECT  
Etc/GMT-1  
Europe/Amsterdam  
Europe/Andorra  
Europe/Belgrade  
Europe/Berlin  
Europe/Bratislava  
Europe/Brussels  
Europe/Budapest  
Europe/Copenhagen  
Europe/Gibraltar  
Europe/Ljubljana  
Europe/Luxembourg  
Europe/Madrid  
Europe/Malta  
Europe/Monaco  
Europe/Oslo  
Europe/Paris  
Europe/Podgorica  
Europe/Prague  
Europe/Rome  
Europe/San_Marino  
Europe/Sarajevo  
Europe/Skopje  
Europe/Stockholm  
Europe/Tirane  
Europe/Vaduz  
Europe/Vatican  
Europe/Vienna  
Europe/Warsaw  
Europe/Zagreb  
Europe/Zurich  
MET  
Poland  
ART  
Africa/Blantyre  
Africa/Bujumbura  
Africa/Cairo  
Africa/Gaborone  
Africa/Harare  
Africa/Johannesburg  
Africa/Kigali  
Africa/Lubumbashi  
Africa/Lusaka  
Africa/Maputo  
Africa/Maseru  
Africa/Mbabane  
Africa/Tripoli  
Asia/Amman  
Asia/Beirut  
Asia/Damascus  
Asia/Gaza  
Asia/Istanbul  
Asia/Jerusalem  
Asia/Nicosia  
Asia/Tel_Aviv  
CAT  
EET  
Egypt  
Etc/GMT-2  
Europe/Athens  
Europe/Bucharest  
Europe/Chisinau  
Europe/Helsinki  
Europe/Istanbul  
Europe/Kaliningrad  
Europe/Kiev  
Europe/Mariehamn  
Europe/Minsk  
Europe/Nicosia  
Europe/Riga  
Europe/Simferopol  
Europe/Sofia  
Europe/Tallinn  
Europe/Tiraspol  
Europe/Uzhgorod  
Europe/Vilnius  
Europe/Zaporozhye  
Israel  
Libya  
Turkey  
Africa/Addis_Ababa  
Africa/Asmara  
Africa/Asmera  
Africa/Dar_es_Salaam  
Africa/Djibouti  
Africa/Kampala  
Africa/Khartoum  
Africa/Mogadishu  
Africa/Nairobi  
Antarctica/Syowa  
Asia/Aden  
Asia/Baghdad  
Asia/Bahrain  
Asia/Kuwait  
Asia/Qatar  
Asia/Riyadh  
EAT  
Etc/GMT-3  
Europe/Moscow  
Europe/Volgograd  
Indian/Antananarivo  
Indian/Comoro  
Indian/Mayotte  
W-SU  
Asia/Riyadh87  
Asia/Riyadh88  
Asia/Riyadh89  
Mideast/Riyadh87  
Mideast/Riyadh88  
Mideast/Riyadh89  
Asia/Tehran  
Iran  
Asia/Baku  
Asia/Dubai  
Asia/Muscat  
Asia/Tbilisi  
Asia/Yerevan  
Etc/GMT-4  
Europe/Samara  
Indian/Mahe  
Indian/Mauritius  
Indian/Reunion  
NET  
Asia/Kabul  
Asia/Aqtau  
Asia/Aqtobe  
Asia/Ashgabat  
Asia/Ashkhabad  
Asia/Dushanbe  
Asia/Karachi  
Asia/Oral  
Asia/Samarkand  
Asia/Tashkent  
Asia/Yekaterinburg  
Etc/GMT-5  
Indian/Kerguelen  
Indian/Maldives  
PLT  
Asia/Calcutta  
Asia/Colombo  
Asia/Kolkata  
IST  
Asia/Kathmandu  
Asia/Katmandu  
Antarctica/Mawson  
Antarctica/Vostok  
Asia/Almaty  
Asia/Bishkek  
Asia/Dacca  
Asia/Dhaka  
Asia/Novosibirsk  
Asia/Omsk  
Asia/Qyzylorda  
Asia/Thimbu  
Asia/Thimphu  
BST  
Etc/GMT-6  
Indian/Chagos  
Asia/Rangoon  
Indian/Cocos  
Antarctica/Davis  
Asia/Bangkok  
Asia/Ho_Chi_Minh  
Asia/Hovd  
Asia/Jakarta  
Asia/Krasnoyarsk  
Asia/Phnom_Penh  
Asia/Pontianak  
Asia/Saigon  
Asia/Vientiane  
Etc/GMT-7  
Indian/Christmas  
VST  
Antarctica/Casey  
Asia/Brunei  
Asia/Choibalsan  
Asia/Chongqing  
Asia/Chungking  
Asia/Harbin  
Asia/Hong_Kong  
Asia/Irkutsk  
Asia/Kashgar  
Asia/Kuala_Lumpur  
Asia/Kuching  
Asia/Macao  
Asia/Macau  
Asia/Makassar  
Asia/Manila  
Asia/Shanghai  
Asia/Singapore  
Asia/Taipei  
Asia/Ujung_Pandang  
Asia/Ulaanbaatar  
Asia/Ulan_Bator  
Asia/Urumqi  
Australia/Perth  
Australia/West  
CTT  
Etc/GMT-8  
Hongkong  
PRC  
Singapore  
Australia/Eucla  
Asia/Dili  
Asia/Jayapura  
Asia/Pyongyang  
Asia/Seoul  
Asia/Tokyo  
Asia/Yakutsk  
Etc/GMT-9  
JST  
Japan  
Pacific/Palau  
ROK  
ACT  
Australia/Adelaide  
Australia/Broken_Hill  
Australia/Darwin  
Australia/North  
Australia/South  
Australia/Yancowinna  
AET  
Antarctica/DumontDUrville  
Asia/Sakhalin  
Asia/Vladivostok  
Australia/ACT  
Australia/Brisbane  
Australia/Canberra  
Australia/Currie  
Australia/Hobart  
Australia/Lindeman  
Australia/Melbourne  
Australia/NSW  
Australia/Queensland  
Australia/Sydney  
Australia/Tasmania  
Australia/Victoria  
Etc/GMT-10  
Pacific/Guam  
Pacific/Port_Moresby  
Pacific/Saipan  
Pacific/Truk  
Pacific/Yap  
Australia/LHI  
Australia/Lord_Howe  
Asia/Magadan  
Etc/GMT-11  
Pacific/Efate  
Pacific/Guadalcanal  
Pacific/Kosrae  
Pacific/Noumea  
Pacific/Ponape  
SST  
Pacific/Norfolk  
Antarctica/McMurdo  
Antarctica/South_Pole  
Asia/Anadyr  
Asia/Kamchatka  
Etc/GMT-12  
Kwajalein  
NST  
NZ  
Pacific/Auckland  
Pacific/Fiji  
Pacific/Funafuti  
Pacific/Kwajalein  
Pacific/Majuro  
Pacific/Nauru  
Pacific/Tarawa  
Pacific/Wake  
Pacific/Wallis  
NZ-CHAT  
Pacific/Chatham  
Etc/GMT-13  
Pacific/Enderbury  
Pacific/Tongatapu  
Etc/GMT-14  
Pacific/Kiritimati  

 

posted @ 2019-08-15 10:20  这个名字想了很久~  阅读(15325)  评论(2编辑  收藏  举报