java zoneinfo中的时区信息_Java程序与操作系统时区不一致问题的处理

1. 问题现象

最近发现某些服务器上运行的java程序选择的时区是Asia/Harbin或Asia/Chungking,而不是我们常见的Asia/Shanghai。由此导致与外部对接方交互时因为时区不同而发生问题。

通过jinfo我们可以查看java程序使用的时区:

[root@centos6 ~]# jinfo 12104 | grep user.timezone

user.timezone = Asia/Harbin

让我们再来看看CentOS的时区:

[root@centos6 ~]# date +"%Z %::z"

CST +08:00:00

2. 原因分析

首先我们来看下Java程序是怎样取得时区信息的。通过Oracle的官方文档,我们可以知道其默认时区的获取方式:

Use the user.timezone property value as the default time zone ID if it's available.

Detect the platform time zone ID. The source of the platform time zone and ID mapping may vary with implementation.

Use GMT as the last resort if the given or detected time zone ID is unknown.

我们可以写一个简单的程序来测试:

DefaultTimeZone.java

public class DefaultTimeZone {

public static void main(String[] args) {

System.out.println(java.util.TimeZone.getDefault());

}

}

[root@centos6 ~]# javac DefaultTimeZone.java

[root@centos6 ~]# java DefaultTimeZone

sun.util.calendar.ZoneInfo[id="Asia/Harbin",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]

果然得到的值是Asia/Harbin。那这个值我们通过date命令看到的CST有什么区别和联系呢? 查阅资料发现,Linux和Java都是通过IANA提供的时区数据库获取时区信息的。我们得到的CST,称为时区简写名称,并且与时区名称是一对多的关系。一个CST可能会对应多个时区名称:

Abbreviation

Time zone name

Location

Offset

CST

Central Standard Time

North America

UTC -6

CST

CT - Central Time

Central America

CST

NACST - North American Central Standard Time

CST

CST - Tiempo Central Estándar (Spanish)

CST

HNC - Heure Normale du Centre (French)

CST

China Standard Time

Asia

UTC +8

CST

Cuba Standard Time

Caribbean

UTC -5

我们上面通过date命令得到的CST,显然是我们的China Standard Time。

而Asia/Harbin或Asia/Shanghai则是IANA时区数据库中提供的时区ID,这个时区ID很有趣,居然与上面国际标准的时区名称也是一对多的关系。这一点我们可以通过保存在Linux系统中的时区数据库(/usr/share/zoneinfo/ 目录内)证实:

[root@localhost ~]# zdump PRC Asia/Shanghai Asia/Chungking Asia/Harbin Asia/Chongqing

PRC Thu Aug 23 15:22:29 2018 CST

Asia/Shanghai Thu Aug 23 15:22:29 2018 CST

Asia/Chungking Thu Aug 23 15:22:29 2018 CST

Asia/Harbin Thu Aug 23 15:22:29 2018 CST

Asia/Chongqing Thu Aug 23 15:22:29 2018 CST

[root@localhost ~]# ls -li /usr/share/zoneinfo/{PRC,Asia/Shanghai,Asia/Chungking,Asia/Harbin,Asia/Chongqing}

2093683 -rw-r--r-- 5 root root 388 May 10 01:41 /usr/share/zoneinfo/Asia/Chongqing

2093683 -rw-r--r-- 5 root root 388 May 10 01:41 /usr/share/zoneinfo/Asia/Chungking

2093683 -rw-r--r-- 5 root root 388 May 10 01:41 /usr/share/zoneinfo/Asia/Harbin

2093683 -rw-r--r-- 5 root root 388 May 10 01:41 /usr/share/zoneinfo/Asia/Shanghai

2093683 -rw-r--r-- 5 root root 388 May 10 01:41 /usr/share/zoneinfo/PRC

IANA的时区数据库中,PRC、Asia/Chongqing、Asia/Chungking、Asia/Harbin、Asia/Shanghai这5个时区ID都是CST时区,并且这些文件其实是同一个文件(相同的inode)。

但是同属中国的Asia/Urumuqi和Asia/Kashgar采用的却是新疆时间(UTC+6)。

IANA采用这些名称作为ID,应该是有历史的原因在里面,这一点我们不深究。但是我们无法理解为什么Java采用的时区ID是Asia/Harbin,而不是我们熟悉的Asia/Shanghai。

由于本人对Linux代码不熟,我无法深入研究其应用的方式,但是从CentOS 7的一个manual文档(是的CentOS 6并没有这个文档)可以发现一些信息:

man 5 localtime

LOCALTIME(5) localtime LOCALTIME(5)

NAME

localtime - Local timezone configuration file

SYNOPSIS

/etc/localtime -> ../usr/share/zoneinfo/...

DESCRIPTION

The /etc/localtime file configures the system-wide timezone of the local system that is used by applications for presentation to the user. It should be an absolute or

relative symbolic link pointing to /usr/share/zoneinfo/, followed by a timezone identifier such as "Europe/Berlin" or "Etc/UTC". The resulting link should lead to the

corresponding binary tzfile(5) timezone data for the configured timezone.

Because the timezone identifier is extracted from the symlink target name of /etc/localtime, this file may not be a normal file or hardlink.

The timezone may be overridden for individual programs by using the $TZ environment variable. See environ(7).

You may use timedatectl(1) to change the settings of this file from the command line during runtime. Use systemd-firstboot(1) to initialize the time zone on mounted (but

not booted) system images.

SEE ALSO

systemd(1), tzset(3), localtime(3), timedatectl(1), systemd-timedated.service(8), systemd-firstboot(1)

systemd 219 LOCALTIME(5)

对于使用systemd的CentOS 7来说,其建议/etc/localtime这个文件是一个到时区数据库的软链接:

Because the timezone identifier is extracted from the symlink target name of /etc/localtime, this file may not be a normal file or hardlink.

在CentOS 7下,也确实如此:

[root@centos7 ~]# ls -li /etc/localtime

33554503 lrwxrwxrwx. 1 root root 35 May 4 13:30 /etc/localtime -> ../usr/share/zoneinfo/Asia/Shanghai

也就是说, 程序会根据软链接的名称来确定时区ID。

回到我们CentOS 6,我们发现其是一个普通的文件,而不是软链接:

[root@centos6 ~]# ls -li /etc/localtime

523326 -rw-r--r--. 1 root root 388 Jan 21 2016 /etc/localtime

让我们改成软链接试试:

[root@centos6 ~]# mv /etc/localtime /etc/localtime.backup && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

[root@centos6 ~]# jinfo 13994 | grep timezone

user.timezone = Asia/Shanghai

看起来问题解决了。

3. 解决方案

从上面分析的情况来看,我们可以从几个方面去解决这个问题:

修改操作系统/etc/localtime。这个我们上面已经提及,只需要软链接到/usr/share/zoneinfo/下正确的时区文件就可以。

启动java时设置环境变量TZ,比如TZ=Asia/Shanghai。

启动java时设置一个全局的变量:

java -Duser.timezone=Asia/Shanghai ....

你可以选择最合适的方式来设置时区。

PS:对于操作系统维护者或者镜像管理者来说,我建议在制作系统镜像时可以将/etc/localtime改为软链接的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值