日期:2014-05-16  浏览次数:20641 次

Java TimeZone 和 Linux TimeZone问题
Java TimeZone 和 Linux TimeZone问题
1人收藏此文章, 我要收藏 发表于1年前 , 已有1777次阅读 共0个评论
昨天发现跑在Linux上的java程序获取的默认时区有问题。

由于我所用Linux的时区由/etc/localtime所指的文件(如果环境变量TZ不存在时):
[xx:~]> ls -l /etc/localtime
lrwxrwxrwx  1 root root 18 Jun 21  2008 /etc/localtime -> /var/etc/localtime
[xx:~]> ls -l /var/etc/localtime
lrwxrwxrwx  1 root root 30 May 14 09:46 /var/etc/localtime -> /usr/share/zoneinfo/US/Eastern

开始时,我以为应该是和/etc/localtime指向的时区一样的,接着才发现原来java在没有TZ环境变量时取的是 /etc/sysconfig/clock

中的时时区。 Sun上面有和我这种情况相关的bug - Default timezone is incorrectly set occasionally on Linux(http://bugs.sun.com/view_bug.do?bug_id=6456628), 里面描述了java vm取的默认timezone的算法:

1)如有环境变量 TZ设置,则用TZ中设置的时区

2)在 /etc/sysconfig/clock文件中找 "ZONE"的值

3)如何2)都没,就用/etc/localtime 和 /usr/share/zoneinfo 下的时区文件进行匹配,如找到匹配的,就返回对应的路径和文件名。



下面是我的测试:

java测试程序(来自:http://www.minaret.biz/tips/timezone.html)

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

public class TimeTest {

    public static void main(String args[]) {
long time = System.currentTimeMillis();
String millis = Long.toString(time);
Date date = new Date(time);
System.out.println("Current time in milliseconds = " + millis + " => " + date.toString());
System.out.println("Current time zone: " + TimeZone.getDefault().getID());
    }
}


查看本地时区设置:

[xx:~]> echo $TZ

(TZ 环境变量没设置)
[xx:~]> ls -l  /var/etc/localtime
lrwxrwxrwx  1 root root 30 May 14 02:24 /var/etc/localtime -> /usr/share/zoneinfo/US/Arizona

[xx:~]>date
Fri May 14 02:30:05 MST 2010

date 命令显示的时间 和 /var/etc/localtime 指向的时间一致

查看/etc/sysconfig/clock中的时区设置(Redhat Linux)
[xx:~]> cat /etc/sysconfig/clock
ZONE="America/New_York"
UTC=false
ARC=false

[xx:~]> java TimeTest
Current time in milliseconds = 1273829564349 => Fri May 14 05:32:44 EDT 2010
Current time zone: America/New_York
Current time zone display: Eastern Standard Time
[xx:~]>

TimeTest运行结果显示,java vm取得的的默认时区和 /etc/sysconfig/clock 中的设置一样。让我们来验证一下:
1)修改/etc/sysconfig/clock:
[xx:~]> vim /etc/sysconfig/clock
ZONE="US/Central"
UTC=false
ARC=false
2)再运行 TimeTest
[xx:~]> java TimeTest
Current time in milliseconds = 1273829718269 => Fri May 14 04:35:18 CDT 2010
Current time zone: US/Central
Current time zone display: Central Standard Time


修改/var/etc/localtime 指向时区
先看看date显示:
[xx:~]> date
Fri May 14 02:36:37 MST 2010
[xx:~]> sudo ln -sf /usr/share/zoneinfo/US/Central  /var/etc/localtime
查看date命令结果的变化
[xx:~]> date
Fri May 14 04:37:41 CDT 2010
可以到时间和时区自动变了


好,再看另外一种情况:当 TZ 这个环境变量存在并有设置时

首先看看TZ的值为空时,date命令结果的变化
[xx:~]> export TZ=
[xx:~]> date
Fri May 14 09:41:04 UTC 2010

时间变了,且时区显示是UTC(Universial Time Coordination).

也看看TimeTest的运行结果:
[xx:~]> java TimeTest
Current time in milliseconds = 1273830175690 => Fri May 14 09:42:55 GMT 2010
Current time zone: GMT
Current time zone display: Greenwich Mean Time
[xx:~]>

可以看出,java vm 默认时区是GMT。

给TZ赋某个时区:

[xx:~]> export TZ="US/Central"
[xx:~]> date

Fri May 14 04:44:40 CDT 2010

date的输出跟着 TZ 变量马上调整过来

运行TimeTest
[xx:~]> java TimeTest
Current time in milliseconds = 1273830328966 => Fri May 14 04:45:28 CDT 2010
Current time zone: US/Central
Current time zone display: Central Standard Time

TimeTest 取得和TZ一样的时区


[xx:~]> sudo ln -sf /usr/share/zoneinfo/US/Eastern /var/etc/localti