JavaのTimeZoneのサマータイムの問題について話します

4700 ワード

元リンク:JavaのTimeZoneサマータイムの問題について
Linuxでsystem-config-dateツールを使用してTimeZoneを設定した後(設定の結果は/etc/sysconfig/clockファイルに記録されます)、サマータイムの出入り時にJavaで取った時間がサマータイムのクロックに従って変化しないため、Javaでのシステム時間が混乱します.
現在のマシンのTimeZone情報を取得し、夏の時間の変化を見る時間を印刷するための小さなプログラムを書きました.
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
 
public class TimeZoneTest {
    public static void main(String[] args) {
        TimeZone tz = TimeZone.getDefault();
        System.out.println("tz: " + tz);
         
        int offset = tz.getRawOffset();
        System.out.println("raw offset: " + offset);
         
        int dstSavings = tz.getDSTSavings();
        System.out.println("dstSavings: " + dstSavings);
         
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        while(true) {
            Calendar cal = Calendar.getInstance();
            String msg = "[" + sdf.format(cal.getTime()) + "] " + cal.getTime();
            msg += ", offset: " + TimeZone.getDefault().getOffset(cal.getTimeInMillis());
            System.out.println(msg);
             
            try {
                Thread.sleep(60 * 1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

次の2つの正しいCaseは、TimeZoneが「America/LOs_Angeles」に設定されている場合に実行される結果です
Case 1:システム時間設定2014-03-09 01:59:00 AM
tz: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
raw offset: -28800000
dstSavings: 3600000
[2014-03-09 01:59:03] Sun Mar 09 01:59:03 PST 2014, offset: -28800000
[2014-03-09 03:00:03] Sun Mar 09 03:00:03 PDT 2014, offset: -25200000
[2014-03-09 03:01:03] Sun Mar 09 03:01:03 PDT 2014, offset: -25200000

Case 2:設定システム時間は2014-11-02 12:59:00 AM
tz: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
raw offset: -28800000
dstSavings: 3600000
[2014-11-02 01:59:32] Sun Nov 02 01:59:32 PDT 2014, offset: -25200000
[2014-11-02 01:00:32] Sun Nov 02 01:00:32 PST 2014, offset: -28800000
[2014-11-02 01:01:32] Sun Nov 02 01:01:32 PST 2014, offset: -28800000

Case 3:TimeZoneを「America/North_Dakota/center」に設定し、システム時間を2014-03-09 01:59:00 AMに設定し、上記のプログラムを実行して発見する
tz: sun.util.calendar.ZoneInfo[id="GMT-06:00",offset=-21600000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
raw offset: -21600000
dstSavings: 0
[2014-03-09 01:59:12] Sun Mar 09 01:59:12 GMT-06:00 2014, offset: -21600000, loader start date: Sun Mar 09 01:59:00 GMT-06:00 2014
[2014-03-09 02:00:12] Sun Mar 09 02:00:12 GMT-06:00 2014, offset: -21600000, loader start date: Sun Mar 09 02:00:00 GMT-06:00 2014
[2014-03-09 02:01:12] Sun Mar 09 02:01:12 GMT-06:00 2014, offset: -21600000, loader start date: Sun Mar 09 02:01:00 GMT-06:00 2014

結果としてTimeZoneID=GMT-06:00,dstSavings=0が見られ,時間が1:59から2:00に変わったときに時間が3:00に後退しなかったことは,Javaが対応するタイムゾーン情報(Javaのバグかもしれない)を見つけていないことを示しているので,Javaは現在のタイムゾーンがサマータイムを使用しているかどうか分からないため,最終的に取得する時間が間違っている.
このような問題は,次の2つの方法で解決できる.
1.TZ環境変数を使用してJavaプログラムを実行する
$ export TZ=America/North_Dakota/Center
$ java TimeZoneTest

2.使用-Duser.timezone=America/North_Java仮想マシンのシステムパラメータとしてDakota/center
java -Duser.timezone=America/North_Dakota/Center TimeZoneTest

また、システムのtimezone情報は、次のコマンドで表示できます.
cat /usr/share/zoneinfo/zone.tab
zdump /usr/share/zoneinfo/*
zdump -v /usr/share/zoneinfo/America/New_York           
zdump -v /etc/localtime
tz_convert /usr/share/zoneinfo/
sudo cat /etc/sysconfig/clock