1. ホーム
  2. java

[解決済み] この2回(1927年)を引き算すると、なぜおかしな結果になるのでしょうか?

2022-03-13 10:41:02

質問

次のプログラムを実行すると、1秒違いの時刻を参照する2つの日付文字列を解析して比較することができます。

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

と出力されます。

353

なぜ ld4-ld3 ではなく 1 (1秒のタイム差から予想されることですが)ではなく 353 ?

日付を1秒後の時刻に変更すると。

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

次に ld4-ld3 は、次のようになります。 1 .


Java のバージョンです。

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone(`TimeZone.getDefault()`):

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

Locale(Locale.getDefault()): zh_CN

解決方法は?

上海では12月31日にタイムゾーンが変更されるのです。

参照 このページ は、1927年の上海の様子です。基本的に1927年末の真夜中に、時計は5分52秒戻りました。つまり、"1927-12-31 23:54:08" は実際には2回発生していて、Javaはこれを そのローカルの日付/時刻の可能性のある瞬間 - だからこその違いです。

タイムゾーンにまつわる奇妙で素晴らしいエピソードがまたひとつ増えました。

EDIT プレスストップ! 歴史が変わる...

のバージョン2013aで再構築すると、元の質問と全く同じ挙動を示さなくなります。 TZDB . 2013aでは、結果は358秒となり、遷移時間は23:54:08ではなく、23:54:03となるのです。

私がこれに気づいたのは、野田時間でこのような質問を集めているためで、その形は ユニットテスト ... このテストは現在変更されていますが、これは、過去のデータでさえ安全ではないことを示すものです。

EDITです。 また歴史が変わった...。

TZDB 2014fでは、変更時刻が1900-12-31に移動し、わずか343秒の変更になりました(なので、その間の時間は tt+1 は344秒です、おわかりでしょうか)。

EDITです。 1900時の遷移に関する質問に答えると、Javaのタイムゾーンの実装では、次のように扱われるようです。 すべて タイムゾーンは、UTC 1900の開始前のどの瞬間も、単にその標準時であるとして扱われます。

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

上記のコードは、私のWindowsマシンでは何も出力されません。したがって、1900年の開始時に標準的なオフセット以外のオフセットを持つタイムゾーンは、それを移行としてカウントします。TZDB自体はそれ以前にさかのぼるデータを持っており、標準時の考え方に依存しない(つまり、これは getRawOffset は有効な概念であると仮定しているので、他のライブラリはこの人工的な移行を導入する必要はありません。