2020年3月30日と3月1日の差が29日ではなく誤って28日になるのはなぜですか?


124
TimeUnit.DAYS.convert(
   Math.abs(
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("30-03-2020 00:00:00").getTime() - 
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("1-03-2020 00:00:00").getTime()
   ),
   TimeUnit.MILLISECONDS)

結果は28ですが、29になるはずです。

タイムゾーン/場所に問題があるのでしょうか?


17
注:SimpleDateFormat廃止されたため、今後は使用しないでください。java.time代わりにのパッケージを使用してください。中SimpleDateFormatの場合、使用DateTimeFormatter。Java 7の場合は、以下のAndy Turnerのコメントを参照してください。
MC皇帝

28
時間に数学をしないでください。適切なタイムライブラリを使用します(java.time[Java 7を使用していることに注意します]、ThreeTenBpJoda)。
アンディターナー

14
私は誰かが行く会議に参加したいと思います。「さて、タイムゾーンがちょっとわかったので、100%arseモードにして、夏時間と呼ばれるこのことを実装します。これは、アシッドの旅の後に夢の中で思いついたものです。昨夜。"
MonkeyZeus

5
@gmauch TimeUnitはDSTについて何も知らないふりをしません。javadocが言うように:ナノ秒は1000分の1マイクロ秒、マイクロ秒は1000分の1ミリ秒、ミリ秒は1000分の1秒、1分は60秒、1時間は60分、1日は次のように定義されます。24時間--- DSTにより、年間の2日間が正確に24時間にTimeUnitなるわけではないため、DSTが関係している場合、それは間違いになります。
アンドレアス

1
この問題は、夏時間のあるタイムゾーンにあるコンピューターでのみ発生します。夏時間のないタイムゾーンでは正しい日数(29)が得られます。
Gopinath

回答:


207

問題は、夏時間シフト(2020年3月8日日曜日)が原因で、それらの日付の間に28日と23時間があることです。結果を28日にTimeUnit.DAYS.convert(...) 切り捨てます。

問題を確認するには(私は米国東部時間帯です):

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

System.out.println(diff);
System.out.println("Days: " + TimeUnit.DAYS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Hours: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Days: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS) / 24.0);

出力

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

修正するには、UTCなどのDSTがないタイムゾーンを使用します。

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

出力

2505600000
Days: 29
Hours: 696
Days: 29.0

121
「修正する」は、時間を使って計算をしないでください。適切な日付/時刻ライブラリを使用してください。
アンディターナー

62
@AndyTurner修正するには、組み込みのJava 7 APIを使用します。それはので可能に固定され、有効な回答であるかを示します。完全なライブラリ(Joda-Time、ThreeTenなど)を含めるように誰かに強制するだけで、この1つの計算だけではやり過ぎになります。もちろん、ライブラリの使用をお勧めしますが、必須ではありませ
アンドレアス

38
私は敬意を払いません。計算をしたい場合は、正しく計算してください。それを正しく行うための費用を支払う。
アンディターナー

16
私の0.02ユーロ:外部ライブラリなしでJava 7で行う「正しい」方法は、GregorianCalendarオブジェクトを使用して、終了日に達するまで一度に1日ずつ追加することです。それを避けるために私は高額を払うでしょう。そして、すでにJava 8、9、10、11、12、13の一部であるライブラリのバックポートを追加することは、高額ではありません。逆に、次に日付または時刻を使用して何かを行う必要がある場合、それはすでに利益になります。
Ole VV

27
UTCでさえ、6月の最終日が1秒と短すぎる場合があり、実際の警告や予測ができません。常に日時ライブラリを使用してください。
Affe

41

この問題の原因は、Andreasの回答にすでに記載されています

問題は正確にをカウントしたいです。実際の差は28ではなく29である必要があると述べ、「場所/ゾーンの時間に問題がある可能性がある」かどうかを尋ねると、実際に何をカウントしたいかがわかります。どうやら、あなたはタイムゾーンの違いを取り除きたいと思っています。

時間とタイムゾーンを除いて、日のみを計算したいと思います。

Java 8

以下では、その間の日数を正しく計算する方法の例で、それを正確に表すクラス(時間とタイムゾーンのない日付)を使用していLocalDateます。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MM-yyyy HH:mm:ss");
LocalDate start = LocalDate.parse("1-03-2020 00:00:00", formatter);
LocalDate end = LocalDate.parse("30-03-2020 00:00:00", formatter);

long daysBetween = ChronoUnit.DAYS.between(start, end);

なおChronoUnitDateTimeFormatterLocalDateによると、あなたに利用できないのJava 8、少なくとも必要とタグを。しかし、それはおそらく将来の読者にとってのものです。

Ole VVで述べたように、Java 8の日付と時刻のAPI機能をJava 6および7にバックポートするThreeTenバックポートもあります。


2
@ OleV.V。ThreeTenがあることは知っていますが、何回か言及したユーザーもいます。(テキストThreeTen;-) を含むユーザー5772882のすべての投稿とコメントを返すSEDEクエリにリンクしようとしていましたが、残念ながら執筆時点ではオフラインです。)投稿を更新します。
MC皇帝

2
@OleVVそれは悪いことではありません。ほとんどの人はについて無知だと思いjava.timeます。学校ではまだ古いクラスを使用しているからです。ただし、Java 8の日付と時刻のAPIは非常によく設計されているため、使用しないと損失になります。
MC皇帝
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.