Java の TimeZone


先日 Java の Web アプリでログに出力される時間が UTC で出力されるという事がありました。日本に住んでいるので JST で表示されないと紛らわしくて困ります。
すぐに思いついたのが /etc/localtime が UTC に対応するものになっていることでしたが、違いました。ちゃんと Asia/Tokyo に対応するものになっており date コマンドの出力も JST で表示されます。次に疑ったのは環境変数です。しかし、/proc/<pid>/environ を見ても TZ などが設定されてはいません。ためしに TZ を設定して起動すると、意図通りログに出力される時間は JST で出力されています。とりあえずこれで直ると言えば直るのですが、どうも気になりますし、気持ち悪いです。

Web を検索していくつかのページを見て見ましたが、出てくるのは /etc/localtime や環境変数と Java のシステムプロパティ user.timezone のことだけです。

まずは検証用の簡単なコードを書きました。

public class Main {
    public static void main(String[] args) {
        System.out.println(new java.text.SimpleDateFormat("z").format(new java.util.Date()));
        System.out.println(java.util.TimeZone.getDefault().getID());
        System.out.println(System.getProperty("user.timezone"));
    }
}

問題のあるサーバ上でこれを実行すると以下の結果を得ました。

$ java Main
UTC
Etc/UTC
Etc/UTC

次に、問題のないサーバで実行すると以下の結果を得ました。

$ java Main
JST
Asia/Tokyo
Asia/Tokyo

いずれのサーバでも /etc/timezone は Asia/Tokyo になっていますし、環境変数やシステムプロパティも設定していません。

何か OS の設定が関係していると仮定し /etc 以下を探してみると…、ありました。/etc/timezone です。問題のあるサーバにはこのファイルがあり、内容は Etc/UTC になっていました。問題のないサーバではこのファイルはありませんでした。早速ファイルの内容を Asia/Tokyo に書き換えて検証コードを実行すると、

$ java Main
JST
Asia/Tokyo
Asia/Tokyo

ビンゴです。

気になったので /etc/timezone の内容は Asia/Tokyo のままにして /etc/localtime を UTC を指すようにして実行してみると

$ cat /etc/timezone 
Asia/Tokyo
$ ls --time-style=iso -l /etc/localtime 
lrwxrwxrwx 1 root root 27 06-25 07:42 /etc/localtime -> /usr/share/zoneinfo/Etc/UTC
$ date +%Z
UTC
$ java Main
JST
Asia/Tokyo
Asia/Tokyo

予想通り /etc/localtime よりも /etc/timezone の方が優先されるようです。

この /etc/timezone は debian 系の Ubuntu にはありましたが Red Hat 系の CentOS や Scientific Linux には確認した限りではありませんでした。ちなみに Red Hat 系のディストリビューションでも /etc/timezone を作ってやるとそちらが優先されました。

今回の検証は Ubuntu 12.04 及び Scientific Linux 6.4 上で行いました。
java.util.TimeZone では最終的に native メソッドを呼んでいるため他の環境では結果が異なるかもしれません。


This entry was posted in Java, プログラミング. Bookmark the permalink.