Próbuję obliczyć różnicę między dwoma LocalDateTime
.
Dane wyjściowe muszą mieć format y years m months d days h hours m minutes s seconds
. Oto co napisałem:
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
public class Main {
static final int MINUTES_PER_HOUR = 60;
static final int SECONDS_PER_MINUTE = 60;
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
public static void main(String[] args) {
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Period period = getPeriod(fromDateTime, toDateTime);
long time[] = getTime(fromDateTime, toDateTime);
System.out.println(period.getYears() + " years " +
period.getMonths() + " months " +
period.getDays() + " days " +
time[0] + " hours " +
time[1] + " minutes " +
time[2] + " seconds.");
}
private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
return Period.between(dob.toLocalDate(), now.toLocalDate());
}
private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
LocalDateTime today = LocalDateTime.of(now.getYear(),
now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
Duration duration = Duration.between(today, now);
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
return new long[]{hours, minutes, secs};
}
}
Otrzymuję wynik 29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. Sprawdziłem swój wynik z tej strony (z wartościami 12/16/1984 07:45:55
i 09/09/2014 19:46:45
). Poniższy zrzut ekranu pokazuje dane wyjściowe:
Jestem prawie pewien, że pola po wartości miesiąca są niepoprawne z mojego kodu. Wszelkie sugestie byłyby bardzo pomocne.
Aktualizacja
Przetestowałem swój wynik z innej strony internetowej i otrzymałem inny wynik. Oto on: Oblicz czas trwania między dwiema datami (wynik: 29 lat, 8 miesięcy, 24 dni, 12 godzin, 0 minut i 50 sekund).
Aktualizacja
Ponieważ otrzymałem dwa różne wyniki z dwóch różnych witryn, zastanawiam się, czy algorytm moich obliczeń jest prawidłowy, czy nie. Jeśli użyję następujących dwóch LocalDateTime
obiektów:
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Potem nadchodzi wynik: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.
Z tego linku powinno być 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds
. Dlatego algorytm musi również obsługiwać liczby ujemne.
Uwaga: pytanie nie dotyczy tego, która strona dała mi jaki wynik, muszę znać odpowiedni algorytm i mieć właściwe wyniki.
Period.between()
zastosować jakieś zaokrąglenie?7:45:55
i godzinę zakończenia19:46:45
(lub7:46:45
PM). Różnica między tymi dwoma czasami wynosi 12 godzin, 0 minut i 50 sekund, a nigdy 23 godziny, 34 minuty i 12 sekund. Więc twoje obliczenia wydają się być poprawne, przynajmniej w części czasu.LocalDateTime
nie ma strefy czasowej, odpowiedź może nie być jednoznaczna. Nawet jeśli założysz, że początkowa i końcowa strefa czasowa są takie same, w niektórych strefach daty, takie jak 2014-09-09 będą miały czas letni lub letni, a w innych nie. To może zrzucić rzeczy o godzinę. Zatem obliczenie różnicy w stosunku do drugiego jest bez znaczenia, chyba że zostanie to rozwiązane.LocalDateTime
daje nierealistyczne wyniki, ponieważ tej klasie celowo brakuje jakiejkolwiek koncepcji strefy czasowej lub przesunięcia względem UTC? Aby uzyskać realistyczne wartości, przypisz strefę czasową zaZoneId
pomocąZonedDateTime
.Odpowiedzi:
Niestety nie wydaje się, że istnieje okres, który obejmuje również czas, więc może być konieczne samodzielne wykonanie obliczeń.
Na szczęście klasy daty i godziny mają wiele metod użyteczności, które w pewnym stopniu upraszczają to. Oto sposób na obliczenie różnicy, choć niekoniecznie najszybszej:
Podstawowa idea jest następująca: utwórz tymczasową datę rozpoczęcia i uzyskaj pełne lata do końca. Następnie dostosuj tę datę o liczbę lat, aby data rozpoczęcia była krótsza niż rok od końca. Powtórz to dla każdej jednostki czasu w kolejności malejącej.
Wreszcie zastrzeżenie : Nie brałem pod uwagę różnych stref czasowych (obie daty powinny znajdować się w tej samej strefie czasowej), a także nie testowałem / sprawdzałem, jak czas letni lub inne zmiany w kalendarzu (takie jak zmiany stref czasowych w Samoa) wpływa na to obliczenie. Więc używaj ostrożnie.
źródło
ChronoUnit
:), ale wykonuje pracę „ręcznie”.long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
zwróci liczbę większą niż 59 dla dowolnego interwału trwającego co najmniej 1 godzinę - i nie tego chce OP. Biorąc to pod uwagę, nie można wykonać 6 połączeńChronoUnit#between()
i wykonać.Znalazłem najlepszy sposób, aby to zrobić ChronoUnit.
Dodatkowa dokumentacja znajduje się tutaj: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
źródło
tempDateTime.until( toDateTime, ChronoUnit.YEARS)
zChronoUnit.YEARS.between(fromDate, toDate)
. Ale ważnych dodatkowych linii, takich jaktempDateTime.plusYears( years )
itp., Całkowicie brakuje w twojej odpowiedzi, więc nie pomaga OP. Cały algorytm ma znaczenie!Oto pojedynczy przykład użycia Duration i TimeUnit, aby uzyskać format „hh: mm: ss”.
źródło
String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());
. Metody części dają odpowiednie liczby do zbudowania łańcucha. Myślę, że to bardziej czytelne.To powinno być prostsze!
źródło
TL; DR
a następnie za pomocą metody
period.getYears()
,period.getMonths()
,period.getDays()
,duration.toHoursPart()
,duration.toMinutesPart()
,duration.toSecondsPart()
.Rozszerzona odpowiedź
Odpowiem na pierwotne pytanie, tj. Jak uzyskać różnicę czasu między dwoma
LocalDateTimes
w latach, miesiącach, dniach, godzinach i minutach, tak aby „suma” (patrz uwaga poniżej) wszystkich wartości dla różnych jednostek była równa sumie różnica czasowa, i takie, że wartość dla każdego modułu jest mniejszy niż kolejnego większa jednostka-IEminutes < 60
,hours < 24
i tak dalej.Biorąc pod uwagę dwa
LocalDateTimes
start
iend
npmożemy reprezentować bezwzględny przedział czasowy między nimi za pomocą -
Duration
może za pomocąDuration.between(start, end)
. Ale największą jednostką, z której możemy wydobyć,Duration
są dni (jako jednostka czasowa równoważna 24 godzinom) - wyjaśnienie znajduje się poniżej. Aby użyć większych jednostek (miesięcy, lat), możemy to przedstawićDuration
za pomocą pary (Period
,Duration
), gdziePeriod
miara różnicy do dokładności dni, aDuration
reprezentuje resztę:Teraz możemy po prostu użyć zdefiniowanych metod
Period
iDuration
wyodrębnić poszczególne jednostki:lub używając domyślnego formatu:
Uwaga na lata, miesiące i dni
Zauważ, że w
java.time
koncepcji „jednostki”, takie jak „miesiąc” lub „rok”, nie reprezentują stałej, bezwzględnej wartości czasowej - zależą one od daty i kalendarza, co ilustruje poniższy przykład:źródło
Istnieje pewien problem z kodem Tapas Bose i kodem Thomas. Jeśli różnica czasu jest ujemna, tablica pobiera wartości ujemne. Na przykład jeśli
zwraca 0 lat 0 miesięcy 1 dni -1 godzin 0 minut 0 sekund.
Myślę, że właściwym wyjściem jest: 0 lat 0 miesięcy 0 dni 23 godzin 0 minut 0 sekund.
Proponuję oddzielić instancje LocalDateTime w instancjach LocalDate i LocalTime. Następnie możemy uzyskać instancje okresu i czasu trwania Java 8. Instancja Duration jest rozdzielana według liczby dni i wartości czasu w ciągu dnia (<24 godziny) z późniejszą korektą wartości okresu. Gdy druga wartość LocalTime jest wcześniejsza niż wartość firstLocalTime, konieczne jest skrócenie tego okresu o jeden dzień.
Oto mój sposób na obliczenie różnicy LocalDateTime:
Powyższą metodę można wykorzystać do obliczenia różnicy lokalnych wartości daty i godziny, na przykład:
Dla powyższej metody wygodnie jest napisać test jednostkowy (oba są członkami klasy PeriodDuration). Oto kod:
}
Wszystkie testy zakończone powodzeniem niezależnie od tego, czy wartość pierwszego LocalDateTime jest wcześniejsza, czy też nie.
źródło
java.time.Period
użytkownicy mogą wymuszać / produkować takie mieszane znaki ze względu na inny projekt wewnętrzny).Wersja @ Thomas w Groovy z zajmuje żądane jednostki na liście zamiast na stałe wpisywać wartości. Ta implementacja (którą można łatwo przenieść na Javę - wyraźnie podałem deklarację funkcji) sprawia, że podejście Thomasa jest bardziej użyteczne.
W momencie pisania tego tekstu powyższy kod zwraca
47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis
. I dla danych wejściowych @Gennady Kolomoets kod jest zwracany23 Hours
.Po podaniu listy jednostek należy je posortować według wielkości jednostek (najpierw największe):
źródło
Oto bardzo prosta odpowiedź na twoje pytanie. To działa.
źródło
Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old
. Nie sądzę, żeby tego było pożądane.Joda-Time
Ponieważ wiele odpowiedzi wymagało obsługi interfejsu API 26, a mój minimalny interfejs API wynosił 23, rozwiązałem go za pomocą poniższego kodu:
źródło
Po ponad pięciu latach odpowiadam na moje pytanie. Myślę, że problem z ujemnym czasem trwania można rozwiązać prostą korektą:
Uwaga: Witryna https://www.epochconverter.com/date-difference poprawnie teraz oblicza różnicę czasu.
Dziękujemy wszystkim za dyskusję i sugestie.
źródło
toHours
,toMinutes
itoSeconds
metodyDuration
. Od Java 9 jeszcze więcej odtoMinutesPart()
itoSecondsPart()
. I nie widzę sensu w zwijaniu godzin, minut i sekund w tablicę tylko po to, aby je ponownie wyjąć. Ogólnie jednak dobra odpowiedź.