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)

Wynikiem jest 28, podczas gdy powinno być 29.

Czy Strefa czasowa / lokalizacja może być problemem?

Author: coder, 2020-02-05

2 answers

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

Aby zobaczyć problem (jestem we wschodniej strefie czasowej USA):

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

Wyjście

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

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

Wyjście

2505600000
Days: 29
Hours: 696
Days: 29.0
 208
Author: Andreas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-05 19:52:44

Przyczyna tego problemu jest już wspomniana w odpowiedzi Andreasa .

Pytanie brzmi co dokładnie chcesz policzyć. Fakt, że stwierdzasz, że rzeczywista różnica powinna wynosić 29 zamiast 28, i pytasz, czy "lokalizacja / czas strefy może być problemem", ujawnia, co naprawdę chcesz policzyć. Najwyraźniej chcesz pozbyć się każdej różnicy w strefie czasowej.

Zakładam, że chcesz obliczyć tylko dni, bez czasu i Strefa czasowa.

Java 8

Poniżej, w przykładzie, w jaki sposób liczba dni pomiędzy może być poprawnie obliczona, 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 Java 8, która nie jest dostępna dla Ciebie, zgodnie z tagiem java-7 . Być może jednak dla przyszłych czytelników.

Jak wspomniał Ole V. V., istnieje również Threeten Backport , który backportuje funkcje API daty i czasu Java 8 do Java 6 i 7.

 40
Author: MC Emperor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-06 06:30:39