私のライブラリTime4J (v3.x は Java 7 で動作し、Android では Time4A で動作します) を使用すると、さまざまなタイムゾーンでの書式設定と解析の広範なサポートを含む完全なソリューションが得られます。例:
// Time4J - transformation from old world
Moment m1 = TemporalType.JAVA_UTIL_DATE.translate(d1);
Moment m2 = TemporalType.JAVA_UTIL_DATE.translate(d2);
System.out.println("Time4J - from j.u.Date: " + SI.SECONDS.between(m1, m2)); // 601
// Time4J - programmatical construction of timestamps (like in your example)
m1 = PlainTimestamp.of(2012, 6, 30, 23, 50, 0).atUTC();
m2 = PlainTimestamp.of(2012, 7, 1, 0, 0, 0).atUTC();
System.out.println("Time4J - from utc-timestamps: " + SI.SECONDS.between(m1, m2)); // 601
// Time4J - parsing zoned inputs
ChronoFormatter<Moment> cf =
ChronoFormatter.ofMomentPattern(
"d. MMM uuuu HH:mm:ss[XXX]", // optional offset
PatternType.CLDR,
Locale.ENGLISH,
EUROPE.PARIS); // only used if the offset is missing in input
m1 = cf.parse("1. Jul 2015 01:50:00");
m2 = cf.parse("1. Jul 2015 09:00:00+09:00");
System.out.println("Time4J - from offsets or zones: " + SI.SECONDS.between(m1, m2)); // 601
// the leap second itself can also be parsed
Moment ls = cf.parse("1. Jul 2015 08:59:60+09:00"); // offset of Tokyo
System.out.println("leap second in 2015: " + ls);
// 2015-06-30T23:59:60Z
さらに、うるう秒を認識しているクロックのサポートと、IANA-TZDB からのうるう秒データの転送がサポートされています。例はDZone に関する私の記事を参照してください。Time4J は ThreetenBP を完全に置き換えることができますが、Java-8 (バージョンを v4.x に変更するだけ) とも相互運用可能であり、ここでの質問の範囲外の多くの機能を提供することに注意してください。
補足:ほとんどの人は、Java-8 への将来の移行を容易にするために (いくつかの import ステートメントを変更するだけで)、明らかに ThreetenBP を選択します。しかし、あなたの場合の問題は、
Java-8 への移行後、Threeten-Extra が解決策を示しているようです。ただし、独自のテストを行ったので、そのライブラリを使用しないことをお勧めします。次のコードを参照してください。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Date d1 = sdf.parse("2012-06-30T23:50:00Z");
Date d2 = sdf.parse("2012-07-01T00:00:00Z");
System.out.println(
"Old world in Posix - ignorant of leap seconds: "
+ (d2.getTime() - d1.getTime()) / 1000);
// 600 (OK, SI-seconds without counting leap second, a limitation of POSIX)
Instant instant1 = d1.toInstant();
Instant instant2 = d2.toInstant();
System.out.println(
"Java 8 without Threeten-Extra: "
+ (instant2.getEpochSecond() - instant1.getEpochSecond()));
// 600 (obviously using more or less fuzzy "rubber seconds", not SI-seconds)
// internally a simple 1:1-mapping of POSIX is applied within 'toInstant()'
UtcInstant utc1 = UtcInstant.of(instant1);
UtcInstant utc2 = UtcInstant.of(instant2);
System.out.println(
"Threeten-Extra-impl of UTC-SLS: "
+ utc1.durationUntil(utc2).getSeconds()); // pitfall, see next output!
// 600 (??? - where is the leap second, should be 601 because of original POSIX-input?!)
System.out.println(
"Threeten-Extra-impl of UTC-SLS: "
+ utc1.durationUntil(utc2)); // <= printing the duration object
// PT10M0.600600601S (OK, here the UTC-SLS-specific fraction of second appears
// Reason: Smoothing starts 1000s before leap second)
// only offset "Z=UTC+00:00" can be parsed
utc1 = UtcInstant.parse("2012-06-30T23:50:00Z");
utc2 = UtcInstant.parse("2012-07-01T00:00:00Z");
System.out.println(
"Threeten-Extra-impl of UTC-SLS: " + utc1.durationUntil(utc2).getSeconds());
// 601 (expected, the only way where seconds are really SI-seconds)
書式設定と解析におけるいくつかの明らかな制限は別として、主な問題はタイム スケール定義の扱いがわかりにくいことのようです。秒の定義は、ここではコンテキストに依存します。たとえば、結果「PT10M0.600600601S」は、UTC-SLS を使用した変換のみを考慮する場合は問題ありませんが、UTC-SLS を介した POSIX から UTC への変換全体を考慮する場合は問題ありません。前に述べたように、POSIX 時間はうるう秒の 10 分前に明確に定義されているため、うるう秒中の POSIX のあいまいさは言い訳にはなりません。
NTPタイムサーバーまたはOSカーネルの内部実装に対処するMarkus Kuhnjava.time
の(公式に期限切れの)提案であるUTC-SLSについて、世界の外では実際にNOBODYが話していることに注意してください。代わりに、Google のような他の人々や企業が、独自の異なるリープ スミアの実装を導入しています。UTC-SLS の未来は見えません。
そして、java.util.Date
UTC-SLS とは何の関係もありません (はるかに古い) が、通常のタイムスタンプについても明確に定義されています (Si 秒の UTC 定義に従って、まったく処理されない閏秒の制限があります)。外部 IT の世界 (うるう秒や UTC-SLS について何も知りたくない) と Threeten-Extra の間の相互運用性の問題で、計算された期間に予期しない違いが生じる可能性があります。