web-dev-qa-db-ja.com

なぜこれら2回(1927年)を引いても奇妙な結果が出るのでしょうか?

次のプログラムを実行すると、2つの日付文字列を1秒間隔で参照して比較し、それらを比較します。

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-ld31ではないのですか(私は時間の1秒の違いから予想されるように)、しかし353はどうですか?

1秒後に日付を時間に変更したとします。

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

ld4-ld31になります。


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
6417
Freewind

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

上海の1927年の詳細については このページ をご覧ください。基本的に1927年末の真夜中に、時計は5分52秒戻りました。だから "1927-12-31 23:54:08"実際には二度起こった、そしてそれはJavaがそのローカルの日付/時間のための後で可能な瞬間としてそれを解析しているように見える - それゆえ違い。

奇妙で素晴らしいタイムゾーンの世界でのもう1つのエピソード。

編集: プレスをやめて!歴史の変化.

_ tzdb _ のバージョン2013aで再構築した場合、元の質問ではまったく同じ動作が示されなくなります。 2013aでは、結果は358秒になり、遷移時間は23:54:08ではなく23:54:03になります。

私はこれに気付いたのは、このような質問をNoda Timeで 単体テスト の形で集めているからです - /...テストは現在変更されていますが、実際には表示されます。

編集: 履歴が再び変更されました...

TZDB 2014fでは、変更の時間は1900年から12月31日に移りましたが、今ではわずか343秒の変更になっています(つまり、tt+1の間の時間は344秒です)。

編集: 1900年の移行に関する質問に答えると... Javaタイムゾーンの実装はallタイムゾーンを開始前の任意の瞬間の標準時間にあるものとして扱うように見えます。 1900 UTCの

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が有効な概念であると仮定しているものです)の考えには依存しないので、他のライブラリはこの人為的な移行を導入する必要はありません。

10176
Jon Skeet

現地時間の不連続性が発生しました

現地標準時が1928年1月1日日曜日に近づいていたとき、 00:00:00時計は、31:00に土曜日の0:05:52時間後退しました。 1927年12月23日54:08現地時間

これは特に奇妙なことではありませんし、政治的または行政的な行動によってタイムゾーンが切り替えられたり変更されたりしたために、随所にかなり頻繁に起こりました。

1523

この奇妙さの教訓は次のとおりです。

  • 可能な限りUTCで日付と時刻を使用してください。
  • UTCで日付または時刻を表示できない場合は、常にタイムゾーンを指定してください。
  • UTCで入力日時を要求できない場合は、明示的に示されたタイムゾーンを要求してください。
611
Raedwald

時間を増やすときは、UTCに戻してから加算または減算します。表示にのみ現地時間を使用してください。

これにより、数時間または数分が2回発生するあらゆる期間を確認できます。

UTCに変換した場合は、毎秒を加算し、表示のために現地時間に変換します。あなたは午後11時54分08秒に通過します _ lmt _ - 11:59:59 p.m. LMT、それから11:54:08 p.m. _ cst _ - 11:59:59 p.m. CST.

338
PatrickO

各日付を変換する代わりに、次のコードを使います。 

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

そして、結果がわかります:

1
284
Rajshri

申し訳ありませんが、時間の不連続性がやや改善しました。

2年前の JDK 6 、および JDK 7 のごく最近の update 25

学ぶべきレッスン:表示以外の場合は、UTC以外の時間を絶対に避けてください。

202
user1050755

他の人が説明したように、そこには時間的な不連続があります。 1927-12-31 23:54:08Asia/Shanghaiには2つのタイムゾーンオフセットがありますが、1927-12-31 23:54:07のオフセットは1つだけです。したがって、どのオフセットを使用するかに応じて、1秒の差または5分53秒の差があります。

私たちが慣れている通常の1時間の夏時間(夏時間)の代わりに、オフセットのこのわずかなシフトは、問題を少し不明瞭にします。

タイムゾーンデータベースの2013a更新により、この不連続性が数秒早く移動しましたが、その効果は依然として観察可能です。

Java 8上の新しいJava.timeパッケージは、これをより明確に見て、それを処理するためのツールを提供します。与えられた:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

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

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

durationAtEarlierOffsetは5分53秒になりますが、durationAtLaterOffsetは1秒になります。

また、これら2つのオフセットは同じです。

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

しかし、これら2つは異なります。

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

1927-12-31 23:59:591928-01-01 00:00:00を比較しても同じ問題が見られますが、この場合、発散が長くなるのは早いオフセットであり、オフセットが2つあるのは早い日付です。

これにアプローチするもう1つの方法は、移行が進行中かどうかを確認することです。これを次のようにして実行できます。

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

isOverlap()isGap()を使用して、トランジションが重複しているか(その場合はその日付/時刻に対して複数の有効なオフセットがあるか)、またはギャップ - その日付/時刻がそのゾーンIDに対して無効であるかを確認できます。 zot4のメソッド.

Java 8が広く利用可能になったとき、またはJSR 310バックポートを採用しているJava 7を使用しているユーザーにとって、これがこの種の問題の処理に役立つことを願っています。

180

私見では、広く普及している、 暗黙的 Javaにおけるローカライズは、その最大の設計上の欠陥です。これはユーザーインターフェイスを対象としているかもしれませんが、率直に言って、プログラマは厳密には対象としていないため、ローカライズを基本的に無視できるIDEを除いて、今日ユーザーインターフェイスに実際にJavaを使用します。あなたはそれを修正することができます(特にLinuxサーバで):

  • lC_ALL = C TZ = UTCをエクスポート
  • システムクロックをUTCに設定します。
  • どうしても必要な場合以外は、ローカライズされた実装を使用しないでください(つまり、表示専用です)。

Java Community Process membersへ私はお勧めします:

  • ローカライズされたメソッドをデフォルトではなくしますが、ローカライズを明示的に要求するようにユーザーに要求します。
  • uTF-8/UTCを代わりに _ fixed _ defaultとして使用してください。これが今日のデフォルトです。このようなスレッドを作りたいのでなければ、他に何かする理由はありません。

つまり、グローバルな静的変数は反OOパターンではないのですか。初歩的な環境変数によって与えられるこれらの普及したデフォルトは他にはありません.......

148
user1050755

他の人が言ったようにそれは上海の1927年の時間の変化です。 

基本的に、その23:54:07は現地の標準時で、その後しばらくして00:00:00で次の日に変わりました。しかしその後、現地標準時のために23:54:08に戻りました。だから、違いは1秒ではなく343秒です。

これはまた米国のような他の場所と台無しにすることができます。アメリカでは夏時間があります。夏時間が始まると、時間が1時間進みます。しかし、しばらくすると夏時間が終了し、標準時間帯に1時間戻ります。そのため、米国で時刻を比較したときの違いは、1秒ではなく3600秒です。

表示のようにUTC以外の時間を使用する必要がない限り、時間が変わらないUTCを使用することをお勧めします。

0
zixuan