次のプログラムを実行すると、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-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"実際には二度起こった、そしてそれはJavaがそのローカルの日付/時間のための後で可能な瞬間としてそれを解析しているように見える - それゆえ違い。
奇妙で素晴らしいタイムゾーンの世界でのもう1つのエピソード。
編集: プレスをやめて!歴史の変化.
_ tzdb _ のバージョン2013aで再構築した場合、元の質問ではまったく同じ動作が示されなくなります。 2013aでは、結果は358秒になり、遷移時間は23:54:08ではなく23:54:03になります。
私はこれに気付いたのは、このような質問をNoda Timeで 単体テスト の形で集めているからです - /...テストは現在変更されていますが、実際には表示されます。
編集: 履歴が再び変更されました...
TZDB 2014fでは、変更の時間は1900年から12月31日に移りましたが、今ではわずか343秒の変更になっています(つまり、t
とt+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
が有効な概念であると仮定しているものです)の考えには依存しないので、他のライブラリはこの人為的な移行を導入する必要はありません。
現地標準時が1928年1月1日日曜日に近づいていたとき、 00:00:00時計は、31:00に土曜日の0:05:52時間後退しました。 1927年12月23日54:08現地時間
これは特に奇妙なことではありませんし、政治的または行政的な行動によってタイムゾーンが切り替えられたり変更されたりしたために、随所にかなり頻繁に起こりました。
この奇妙さの教訓は次のとおりです。
各日付を変換する代わりに、次のコードを使います。
long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);
そして、結果がわかります:
1
他の人が説明したように、そこには時間的な不連続があります。 1927-12-31 23:54:08
のAsia/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:59
と1928-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を使用しているユーザーにとって、これがこの種の問題の処理に役立つことを願っています。
私見では、広く普及している、 暗黙的 Javaにおけるローカライズは、その最大の設計上の欠陥です。これはユーザーインターフェイスを対象としているかもしれませんが、率直に言って、プログラマは厳密には対象としていないため、ローカライズを基本的に無視できるIDEを除いて、今日ユーザーインターフェイスに実際にJavaを使用します。あなたはそれを修正することができます(特にLinuxサーバで):
Java Community Process membersへ私はお勧めします:
つまり、グローバルな静的変数は反OOパターンではないのですか。初歩的な環境変数によって与えられるこれらの普及したデフォルトは他にはありません.......
他の人が言ったようにそれは上海の1927年の時間の変化です。
基本的に、その23:54:07
は現地の標準時で、その後しばらくして00:00:00
で次の日に変わりました。しかしその後、現地標準時のために23:54:08
に戻りました。だから、違いは1秒ではなく343秒です。
これはまた米国のような他の場所と台無しにすることができます。アメリカでは夏時間があります。夏時間が始まると、時間が1時間進みます。しかし、しばらくすると夏時間が終了し、標準時間帯に1時間戻ります。そのため、米国で時刻を比較したときの違いは、1秒ではなく3600
秒です。
表示のようにUTC以外の時間を使用する必要がない限り、時間が変わらないUTCを使用することをお勧めします。