Dlaczego różnica między 30 marca a 1 marca 2020 r. błędnie daje 28 dni zamiast 29?

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)

Wynik to 28 i powinno być 29. Czy strefa czasowa/lokalizacja może być problemem?

Uwagi dotyczące pytania (6)
Rozwiązanie

Problem polega na tym, że z powodu zmiany czasu na czas letni (w niedzielę 8 marca 2020 r.), pomiędzy tymi datami jest 28 dni i 23 godziny. TimeUnit.DAYS.convert(...) skraca wynik do 28 dni.

Aby zobaczyć problem (jestem w amerykańskiej wschodniej strefie czasowej):

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

Output

2502000000
Dni: 28
Godziny: 695
Dni: 28.95833333332

Aby naprawić, użyj strefy czasowej, która nie ma DST, np. 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();

Output

2505600000
Dni: 29
Godziny: 696
Dni: 29,0
Komentarze (15)

Przyczyna tego problemu jest już wspomniana w odpowiedzi Andreasa.

Pytanie brzmi co dokładnie chcesz policzyć. Fakt, że stwierdzasz, iż rzeczywista różnica powinna wynosić 29 zamiast 28, i pytasz, czy "lokalizacja/czas strefy może stanowić problem ", ujawnia, co tak naprawdę chcesz policzyć. Najwyraźniej, chcesz pozbyć się wszelkich różnic czasowych.

Zakładam, że chcecie tylko obliczyć dni, bez czasu i strefy czasowej.

Java 8

Poniżej, w przykładzie jak można poprawnie obliczyć liczbę dni pomiędzy, używam klasy, która reprezentuje dokładnie to - datę bez czasu i strefy czasowej - 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);

Zauważ, że ChronoUnit, DateTimeFormatter i LocalDate wymagają co najmniej Javy 8, która nie jest dostępna dla Ciebie, zgodnie z tagiem [tag:java-7]. Jednakże, być może jest ona dostępna dla przyszłych czytelników.

Jak wspomniał Ole V.V., istnieje również ThreeTen Backport, który backportuje funkcjonalność Java 8 Date and Time API do Java 6 i 7.

Komentarze (2)