Warum gibt der Unterschied zwischen dem 30. März und dem 1. März 2020 fälschlicherweise 28 statt 29 Tage an?

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)

Das Ergebnis ist 28, während es 29 sein sollte.

Könnte die Zeitzone / der Ort das Problem sein?

Joe
quelle
17
Hinweis: Bitte nicht SimpleDateFormatmehr verwenden, da es veraltet ist. Verwenden Sie java.timestattdessen Pakete von . In diesem SimpleDateFormatFall verwenden DateTimeFormatter. Im Fall von Java 7 siehe Andy Turners Kommentar unten.
MC Emperor
28
Rechne nicht mal. Verwenden Sie eine geeignete Zeitbibliothek ( java.time[obwohl ich feststelle, dass Sie Java 7 verwenden], ThreeTenBp , Joda).
Andy Turner
14
Ich wäre gerne in der Besprechung gewesen, in der jemand sagt: "Okay, jetzt, wo wir Zeitzonen haben, haben wir herausgefunden, dass wir zu 100% in den Arschmodus gehen und dieses Ding namens Sommerzeit implementieren, das mir nach meiner Säurereise in einem Traum einfiel letzter Nacht."
MonkeyZeus
5
@gmauch TimeUnitgibt nicht vor, etwas über die Sommerzeit zu wissen. Wie der Javadoc sagt: Eine Nanosekunde ist definiert als eine Tausendstel einer Mikrosekunde, eine Mikrosekunde als eine Tausendstel Millisekunde, eine Millisekunde als eine Tausendstelsekunde, eine Minute als sechzig Sekunden, eine Stunde als sechzig Minuten und ein Tag als vierundzwanzig Stunden . --- Da die Sommerzeit dazu führt, dass 2 Tage im Jahr nicht genau 24 Stunden sind, ist TimeUnitdies falsch, wenn die Sommerzeit betroffen ist.
Andreas
1
Dieses Problem tritt nur auf Computern auf, die sich in Zeitzonen mit Sommerzeit befinden. Es gibt die richtige Anzahl von Tagen (29) in Zeitzonen ohne Sommerzeit an!
Gopinath

Antworten:

207

Das Problem ist, dass aufgrund der Sommerzeitverschiebung (am Sonntag, 8. März 2020) zwischen diesen Daten 28 Tage und 23 Stunden liegen . TimeUnit.DAYS.convert(...) schneidet das Ergebnis auf 28 Tage ab.

Um das Problem zu sehen (ich bin in der Zeitzone US Eastern):

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);

Ausgabe

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

Verwenden Sie zum Beheben eine Zeitzone ohne Sommerzeit, z. B. UTC :

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();

Ausgabe

2505600000
Days: 29
Hours: 696
Days: 29.0
Andreas
quelle
121
"Um zu reparieren" rechnen Sie nicht mit der Zeit. Verwenden Sie eine geeignete Datums- / Zeitbibliothek.
Andy Turner
62
@AndyTurner Zum Beheben mithilfe integrierter Java 7-APIs. Da es kann festgelegt werden, die zeigen , wie eine gültige Antwort. Jemanden zu zwingen, eine vollständige Bibliothek (Joda-Time, ThreeTen usw.) einzuschließen, um nur diese eine Berechnung durchzuführen, wäre übertrieben. Sicher, die Verwendung einer Bibliothek wird empfohlen, ist aber nicht erforderlich .
Andreas
38
Ich bin respektvoll anderer Meinung. Wenn Sie eine Berechnung durchführen möchten, machen Sie es richtig. Zahlen Sie die Kosten, um es richtig zu machen.
Andy Turner
16
Meine € 0,02: Der „richtige“ Weg, in Java 7 ohne externe Bibliothek auszukommen, besteht darin, ein GregorianCalendarObjekt zu verwenden und jeweils 1 Tag hinzuzufügen, bis das Enddatum erreicht ist. Ich würde einen hohen Preis zahlen, um das zu vermeiden. Das Hinzufügen des Backports einer Bibliothek, die bereits Teil von Java 8, 9, 10, 11, 12, 13, ... ist, ist kein hoher Preis. Im Gegenteil, wenn Sie das nächste Mal etwas mit Datum oder Uhrzeit tun müssen, ist dies bereits ein Gewinn.
Ole VV
27
Selbst in UTC ist der letzte Tag im Juni gelegentlich eine Sekunde zu kurz, ohne echte Warnung oder Vorhersehbarkeit. Verwenden Sie immer eine Datums- / Uhrzeitbibliothek.
Affe
41

Die Ursache dieses Problems ist bereits in Andreas 'Antwort erwähnt .

Die Frage ist, was genau Sie zählen möchten. Die Tatsache, dass Sie angeben, dass der tatsächliche Unterschied 29 statt 28 betragen sollte, und fragen, ob "Ort / Zonenzeit ein Problem sein könnte" , zeigt, was Sie tatsächlich zählen möchten. Anscheinend möchten Sie Zeitzonenunterschiede beseitigen.

Ich gehe davon aus, dass Sie nur die Tage ohne Zeit und Zeitzone berechnen möchten.

Java 8

Im Beispiel, wie die Anzahl der Tage zwischen korrekt berechnet werden kann, verwende ich eine Klasse, die genau das darstellt - ein Datum ohne Zeit und Zeitzone 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);

Beachten Sie, dass ChronoUnit, DateTimeFormatterundLocalDate benötigt mindestens Java 8, die Sie nicht zur Verfügung steht, nach dem - Tag. Es ist jedoch vielleicht für zukünftige Leser.

Wie von Ole VV erwähnt, gibt es auch den ThreeTen Backport , der die Java 8-API-Funktionalität für Datum und Uhrzeit auf Java 6 und 7 zurückportiert .

MC Kaiser
quelle
2
@ OleV.V. Ich weiß, dass es ThreeTen gibt, einige Benutzer haben es vielleicht ein paar Mal erwähnt. (Ich wollte gerade auf eine SEDE-Abfrage verlinken, die alle Beiträge und Kommentare des Benutzers 5772882 mit dem Text zurückgibt ;-) ThreeTen, aber leider ist sie zum Zeitpunkt des Schreibens offline.) Ich werde den Beitrag aktualisieren.
MC Emperor
2
@OleVV Es ist überhaupt keine schlechte Sache. Ich denke, die meisten Leute kennen sich einfach nicht aus java.time, weil sie in der Schule immer noch die alten Klassen benutzen. Aber die Java 8 Datum und Uhrzeit API ist sehr gut gestaltet - es wäre ein Verlust nicht zu verwenden.
MC Emperor