Obliczanie różnicy między dwoma instancjami daty Java

433

Korzystam z java.util.Dateklasy Java w Scali i chcę porównać Dateobiekt z bieżącą godziną. Wiem, że mogę obliczyć różnicę za pomocą getTime ():

(new java.util.Date()).getTime() - oldDate.getTime()

Jednak to pozostawia mi tylko longreprezentujące milisekundy. Czy istnieje prostszy i przyjemniejszy sposób na uzyskanie delty czasu?

pr1001
źródło
10
Dlaczego nie ma miłości do czasu Joda? To prawie najlepsza opcja, jeśli masz do czynienia z datami w Javie.
Doktor Jones
7
Proszę sprawdzić moje eleganckie rozwiązanie 2-liniowe, bez użycia Jody i dając wynik w dowolnym TimeUnit na stackoverflow.com/a/10650881/82609
Sebastien Lorber
2
Wstydź się wszystkim, którzy polecają czas
Jody
2
@ Zizouz212 Jeśli chodzi o rekomendowanie Joda-Time, stare klasy daty i czasu w pakiecie z Javą są złe, naprawdę złe, źle zaprojektowane, mylące i kłopotliwe. Tak źle, że Joda-Time odniósł ogromny sukces jako zastępca. Tak źle, że nawet Sun / Oracle zrezygnowało z nich i przyjęło pakiet java.time jako część Java 8 i nowszych. Zajęcia java.time są inspirowane Joda-Time. Zarówno Joda-Time, jak i java.time są prowadzone przez tego samego człowieka, Stephena Colbourne'a . Powiedziałbym, „Hańba każdemu polecić stosowanie Date, Calendari SimpleDateFormat”.
Basil Bourque,
2
Chociaż Joda Time była prawdopodobnie dobrą odpowiedzią na zadane pytanie, dziś najlepszą odpowiedzią dla każdego, kto może korzystać z Java 8, jest użycie java.time.Periodi / lub Duration. Zobacz odpowiedź Basil Bourque poniżej .
Ole VV

Odpowiedzi:

198

DateInterfejs API JDK jest niestety strasznie zepsuty. Polecam korzystanie z biblioteki Joda Time .

Joda Time ma pojęcie przedziału czasu :

Interval interval = new Interval(oldTime, new Instant());

EDYCJA: Nawiasem mówiąc, Joda ma dwie koncepcje: Intervaldo reprezentowania przedziału czasu między dwoma momentami czasu (reprezentuje czas między 8 rano a 10 rano) i a, Durationktóry reprezentuje czas bez rzeczywistych granic czasowych (np. Reprezentuje dwie godziny!)

Jeśli zależy Ci tylko na porównaniach czasu, większość Dateimplementacji (w tym JDK) implementuje Comparableinterfejs, który pozwala na użycieComparable.compareTo()

notnoop
źródło
btw - masz na myśli Comparable.compareTo(), nie Comparable.compare().
Scott Morrison,
Joda-Time ma trzy klasy, które przedstawiają przedział czasu na różne sposoby: Interwał, Czas trwania i Okres. Ta poprawna odpowiedź omawia pierwsze dwa. Zobacz moją odpowiedź, aby uzyskać informacje o okresie.
Basil Bourque,
Java 8 ma nowy interfejs API daty i godziny, taki jak Joda.
tbodt
11
Nowy java.timepakiet w Javie 8 został zainspirowany Joda-Time, ale nie jest zamiennikiem drop-in. Każdy ma swoje zalety i wady. Na szczęście nie musisz wybierać między nimi. Wykorzystaj każdy z nich ze względu na swoje mocne strony, o ile będziesz ostrożny ze swoimi importstwierdzeniami. Zobacz inną odpowiedź, na przykład java.time.
Basil Bourque,
6
Do Twojej wiadomości, projekt Joda-Time jest teraz w trybie konserwacji , a zespół doradza migrację do klas java.time . Zobacz samouczek Oracle .
Basil Bourque
550

Prosty plik różnicowy (bez biblioteki lib)

/**
 * Get a diff between two dates
 * @param date1 the oldest date
 * @param date2 the newest date
 * @param timeUnit the unit in which you want the diff
 * @return the diff value, in the provided unit
 */
public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) {
    long diffInMillies = date2.getTime() - date1.getTime();
    return timeUnit.convert(diffInMillies,TimeUnit.MILLISECONDS);
}

A potem możesz zadzwonić:

getDateDiff(date1,date2,TimeUnit.MINUTES);

aby uzyskać różnicę między 2 datami w jednostkach minut.

TimeUnitjest java.util.concurrent.TimeUnitstandardowym wyliczeniem Java przechodzącym od nanosów do dni.


Diff czytelny dla człowieka (bez lib)

public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {

    long diffInMillies = date2.getTime() - date1.getTime();

    //create the list
    List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
    Collections.reverse(units);

    //create the result map of TimeUnit and difference
    Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
    long milliesRest = diffInMillies;

    for ( TimeUnit unit : units ) {

        //calculate difference in millisecond 
        long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
        long diffInMilliesForUnit = unit.toMillis(diff);
        milliesRest = milliesRest - diffInMilliesForUnit;

        //put the result in the map
        result.put(unit,diff);
    }

    return result;
}

http://ideone.com/5dXeu6

Wyjście jest coś w rodzaju Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}z zamówionymi jednostkami.

Musisz tylko przekonwertować tę mapę na ciąg przyjazny dla użytkownika.


Ostrzeżenie

Powyższe fragmenty kodu obliczają prostą różnicę między 2 instancjami. Może to powodować problemy podczas przełączania czasu letniego, jak wyjaśniono w tym poście . Oznacza to, że jeśli obliczasz różnicę między datami bez godziny, możesz mieć brakujący dzień / godzinę.

Moim zdaniem różnica dat jest subiektywna, szczególnie w dni. Możesz:

  • policz liczbę godzin, które upłynęły: dzień + 1 - dzień = 1 dzień = 24 godziny

  • policz liczbę upływającego czasu, dbając o oszczędność światła dziennego: dzień + 1 - dzień = 1 = 24h (ale używając północy i czasu letniego może to być 0 dzień i 23 godziny)

  • policz liczbę day switches, co oznacza dzień + 1 1pm - dzień 11 rano = 1 dzień, nawet jeśli upłynął tylko 2 godziny (lub 1 godzina, jeśli jest czas letni: p)

Moja odpowiedź jest prawidłowa, jeśli twoja definicja daty różni się w dniach odpowiada 1. przypadkowi

Z JodaTime

Jeśli korzystasz z JodaTime, możesz uzyskać różnicę dla dwóch chwil (millies wspieranych ReadableInstant) z:

Interval interval = new Interval(oldInstant, new Instant());

Ale możesz także uzyskać różnicę dla lokalnych dat / godzin:

// returns 4 because of the leap year of 366 days
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5), PeriodType.years()).getYears() 

// this time it returns 5
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5+1), PeriodType.years()).getYears() 

// And you can also use these static methods
Years.yearsBetween(LocalDate.now(), LocalDate.now().plusDays(365*5)).getYears()
Sebastien Lorber
źródło
Wow, naprawdę możesz nauczyć starego psa nowych sztuczek! Nigdy wcześniej o tym nie słyszałem, bardzo przydatne do tłumaczenia różnic dat na znaczące ciągi.
Jeremy Goodell,
@SebastienLorber Czy w TimeUnit istnieje sposób na obliczenie różnicy? „Alarm jest ustawiony na 3 dni, 4 godziny i 12 minut od teraz”.
likejudo
wspaniałe rzeczy naprawdę zaoszczędziły dużo czasu. Doceniam Twoją pomoc.
Lyju I Edwinson
Zmarnowałem 3 dni życia, zajmując się datami Java, po prostu wyrzuciłem wiele z nich i zastąpiłem tym. Dziękuję Ci.
user1091524
To najlepsza odpowiedź, jaką widziałem odkąd zacząłem na stackoverflow! : D: D: D
Cold
153
int diffInDays = (int)( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) )

Pamiętaj, że działa to z datami UTC, więc różnica może być dniem wolnym od pracy, jeśli spojrzysz na lokalne daty. A poprawne działanie z lokalnymi datami wymaga zupełnie innego podejścia ze względu na czas letni.

Michael Borgwardt
źródło
6
To faktycznie nie działa poprawnie w Androidzie. Występują błędy zaokrąglania. Przykład 19 do 21 maja mówi 1 dzień, ponieważ rzuca 1,99 na 1. Użyj rundy przed rzuceniem na int.
Pratik Mandrekar
4
To najlepsza i najprostsza odpowiedź. Przy obliczaniu różnicy między dwiema datami lokalne strefy czasowe odejmują się nawzajem ... tak, że poprawną odpowiedź (jako podwójną) podaje po prostu ((podwójny) (newer.getTime () - senior.getTime ()) / (86400.0 * 1000.0); ... jako podwójny masz również ułamkowy dzień, który można łatwo przekonwertować na GG: MM: ss.
scottb
8
@scottb: problem z lokalnymi datami polega na tym, że możesz mieć czas letni, co oznacza, że ​​niektóre dni mają 23 lub 25 godzin, co może popsuć wynik.
Michael Borgwardt
1
@ Steve: dla mnie jest to 28, kiedy definiuję je jak nową datę (115, 2, 26); (zwróć uwagę na dokument API dla parametrów). Czy to możliwe, że analizujesz je przy użyciu amerykańskiego formatu daty i w trybie łagodnym, aby 26.03.2015 był interpretowany jako 3. dzień 26. miesiąca 2015?
Michael Borgwardt,
1
@ Steve: hm, nie wiem, dlaczego wcześniej otrzymałem inny wynik, ale teraz mogę odtworzyć błąd. Nawiasy klamrowe w twoim kodzie są inne niż w mojej odpowiedzi, co powoduje (endDate.getTime() - startDate.getTime())rzutowanie na intzamiast wyniku końcowego i otrzymujesz przepełnienie liczb całkowitych.
Michael Borgwardt,
54

Korzystając ze środowiska java.time wbudowanego w Javę 8+:

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println("ISO-8601: " + duration);
System.out.println("Minutes: " + duration.toMinutes());

Wynik:

ISO-8601: PT24H10M

Minuty: 1450

Aby uzyskać więcej informacji, zobacz samouczek Oracle i standard ISO 8601 .

Vitalii Fedorenko
źródło
5
To lub użyj Periodzamiast Durationdat.
Aphex
53

Musisz jaśniej zdefiniować swój problem. Ty mógł po prostu wziąć liczbę milisekund między dwoma Dateobiektami i podzielić przez liczbę milisekund w ciągu 24 godzin, na przykład ... ale:

  • To nie uwzględnia stref czasowych - Datezawsze jest w UTC
  • Nie uwzględni to czasu letniego (na przykład w dni, które mają tylko 23 godziny)
  • Ile dni jest jeszcze w UTC, od 16 sierpnia do 23 sierpnia do 18 sierpnia o 2 rano? To tylko 27 godzin, więc czy to oznacza jeden dzień? A może powinny to być trzy dni, ponieważ obejmują trzy daty?
Jon Skeet
źródło
1
Myślałem java.util. Data była tylko niewielkim opakowaniem wokół reprezentacji czasu-milis, interpretowanej w lokalnej (domyślnej) strefie czasowej. Wydrukowanie daty daje mi lokalną reprezentację strefy czasowej. Czy się tu mylę?
Adriaan Koster,
46

tl; dr

Konwersja przestarzałych java.util.Dateobiektów do ich wymiany, java.time.Instant. Następnie oblicz czas, który upłynął jako Duration.

Duration d = 
    Duration.between(                   // Calculate the span of time between two moments as a number of hours, minutes, and seconds.
        myJavaUtilDate.toInstant() ,    // Convert legacy class to modern class by calling new method added to the old class.
        Instant.now()                   // Capture the current moment in UTC. About two and a half hours later in this example.
    )
;

d.toString (): PT2H34M56S

d. do Minut (): 154

d.toMinutesPart (): 34

Format ISO 8601: PnYnMnDTnHnMnS

Rozsądna norma ISO 8601 definiuje zwięzłą tekstową reprezentację przedziału czasu jako liczbę lat, miesięcy, dni, godzin itp. Norma określa taki przedział czasu . Format oznacza, PnYnMnDTnHnMnSże Poznacza „okres”, Toddziela część daty od części czasu, a pomiędzy nimi są liczby, po których następuje litera.

Przykłady:

  • P3Y6M4DT12H30M5S
    trzy lata, sześć miesięcy, cztery dni, dwanaście godzin, trzydzieści minut i pięć sekund
  • PT4H30M
    Cztery i pół godziny

java.time

Java.time ramy wbudowany w Java 8 i później wypiera kłopotliwe stare java.util.Date/ java.util.Calendarklas. Nowe klasy inspirowane są bardzo udanym frameworkiem Joda-Time , przeznaczonym jako jego następca, podobnym w koncepcji, ale przebudowanym. Zdefiniowane przez JSR 310 . Rozszerzony o projekt ThreeTen-Extra . Zobacz samouczek .

Za chwilę

InstantKlasa reprezentuje moment na osi czasu w UTC z rozdzielczością nanosekund (do dziewięciu (9) cyfr ułamka dziesiętnego).

Instant instant = Instant.now() ;  // Capture current moment in UTC.

Najlepiej unikać klas starszych, takich jak Date/ Calendar. Ale jeśli musisz współpracować ze starym kodem, który nie został jeszcze zaktualizowany do java.time , konwertuj tam iz powrotem. Wywołaj nowe metody konwersji dodane do starych klas. Aby przejść z A java.util.Datedo A Instant, zadzwoń Date::toInstant.

Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy `java.util.Date` class to modern `java.time.Instant` class.

Przedział czasu

Klasy java.time podzieliły ideę reprezentowania przedziału czasu jako liczby lat, miesięcy, dni, godzin, minut, sekund na dwie połowy:

  • Period przez lata, miesiące, dni
  • Duration przez dni, godziny, minuty, sekundy

Oto przykład.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime future = now.plusMinutes ( 63 );
Duration duration = Duration.between ( now , future );

Zrzut do konsoli.

Oba Periodi Durationużywają standardu ISO 8601 do generowania ciągu reprezentującego ich wartość.

System.out.println ( "now: " + now + " to future: " + now + " = " + duration );

teraz: 2015-11-26T00: 46: 48.016-05: 00 [Ameryka / Montreal] do przyszłości: 2015-11-26T00: 46: 48.016-05: 00 [Ameryka / Montreal] = PT1H3M

Java 9 dodaje metody do Duration uzyskać część dni, część godzin, część minut i część sekund.

Możesz uzyskać łączną liczbę dni, godzin, minut lub sekund lub milisekund lub nanosekund w całym okresie trwania.

long totalHours = duration.toHours();

W Javie 9 Durationklasa otrzymuje nowe metody zwracania różnych części dni, godzin, minut, sekund, milisekund / nanosekund. Wywołać to…Partmetody: toDaysPart(), toHoursPart(), i tak dalej.

ChronoUnit

Jeśli zależy ci tylko na prostszym, większym ziarnistości czasu, takim jak „liczba dni, które upłynęły”, użyj ChronoUnitwyliczenia.

long daysElapsed = ChronoUnit.DAYS.between( earlier , later );

Inny przykład.

Instant now = Instant.now();
Instant later = now.plus( Duration.ofHours( 2 ) );

long minutesElapsed = ChronoUnit.MINUTES.between( now , later );

120


O java.time

Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, iSimpleDateFormat .

Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do java.time.

Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj stos przepełnienia dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Gdzie można uzyskać klasy java.time?

Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak Interval, YearWeek, YearQuarter, i więcej .


Joda-Time

AKTUALIZACJA: Projekt Joda-Time jest teraz w trybie konserwacji , a zespół doradza migrację do klas java.time . Zostawiam tę sekcję nietkniętą dla historii.

Biblioteka Joda-Time używa ISO 8601 jako wartości domyślnej. Jej Periodklasa Analizuje i generuje te PnYnMnDTnHnMnS sznurki.

DateTime now = DateTime.now(); // Caveat: Ignoring the important issue of time zones.
Period period = new Period( now, now.plusHours( 4 ).plusMinutes( 30));
System.out.println( "period: " + period );

Renderuje:

period: PT4H30M
Basil Bourque
źródło
Zabawne, że stworzyłeś niepotrzebną ścianę tekstu z „tl; dr”. Pytanie nie dotyczy tego, jak uzyskać znacznik czasu z X temu lub w X jednostkach czasu, ale jak uzyskać różnicę między dwiema datami. Otóż ​​to.
Buffalo,
@Buffalo Nie rozumiem twojego komentarza. Moja odpowiedź dotyczy Period, Durationoraz ChronoUnittrzy klasy, których celem jest określenie delty między punktami czasu. Proszę wskazać, które części mojej „ściany tekstu” są nieistotne. I nie ma racji, mówiąc, że Pytanie zadało „deltę między datami”: Pytanie zadało deltę między Dateobiektami, które są momentami data-czas, a nie „datami”, pomimo niefortunnego nazywania tej klasy.
Basil Bourque,
W twoim komentarzu nie ma nic, co obsługuje dwa istniejące obiekty java.util.Date. Próbowałem użyć różnych fragmentów kodu w swoim kodzie i nie znalazłem niczego, co używa obiektu java.util.Date.
Buffalo
@Buffalo W porządku, dobra krytyka. Dodałem kod w sekcji „tl; dr” konwertując z a java.util.Datena a Instant(tylko wywołanie .toInstant). I dodałem Momentsekcję do wyjaśnienia. Wspomniałeś o parze Dateobiektów, ale w rzeczywistości Pytanie wykorzystuje tylko jeden istniejący Dateobiekt, a następnie przechwytuje bieżący moment, więc zrobiłem to samo. Dzięki za opinie! Wskazówka: zrezygnuj z używania Date- te starsze klasy są naprawdę okropnym nieszczęsnym bałaganem.
Basil Bourque,
23

Nieco prostsza alternatywa:

System.currentTimeMillis() - oldDate.getTime()

Co do „ładniejszego”: cóż, czego dokładnie potrzebujesz? Problem z reprezentowaniem czasu trwania jako liczby godzin i dni itp. Polega na tym, że może to prowadzić do niedokładności i błędnych oczekiwań ze względu na złożoność dat (np. Dni mogą mieć 23 lub 25 godzin z powodu czasu letniego).

Michael Borgwardt
źródło
22

Korzystanie z podejścia milisekundowego może powodować problemy w niektórych lokalizacjach.

Weźmy na przykład różnicę między dwiema datami 03/24/2007 i 03/25/2007 powinna wynosić 1 dzień;

Jednak korzystając z trasy milisekundowej otrzymasz 0 dni, jeśli uruchomisz ją w Wielkiej Brytanii!

/** Manual Method - YIELDS INCORRECT RESULTS - DO NOT USE**/  
/* This method is used to find the no of days between the given dates */  
public long calculateDays(Date dateEarly, Date dateLater) {  
   return (dateLater.getTime() - dateEarly.getTime()) / (24 * 60 * 60 * 1000);  
} 

Lepszym sposobem na wdrożenie tego jest użycie java.util.Calendar

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  
zasadnyy
źródło
19
Czy mógłbyś podziękować oryginalnemu autorowi tego kodu i zdania trzeciego, którego wpis na blogu pochodzi z 2007 roku ?
wchargin
Czy to nie jest trochę nieskuteczne?
Gangnus,
To nie działa poprawnie. daysBetween (new GregorianCalendar (2014,03,01), new GregorianCalendar (2014,04,02))); zwraca 31, a powinien zwrócić 32: timeanddate.com/date/…
marcolopes
5
@marcolopes - Mylisz się - ponieważ miesiące kalendarzowe są zerowe. Jestem pewien, że masz na myśli marzec / kwiecień, ale to, co testujesz, to kwiecień / czerwiec, który wynosi 31. Dla bezpieczeństwa napisz to jak -> nowy GregorianCalendar (2014, Calendar.MARCH, 1) ....
Wujek Iroh
@UncleIroh, masz rację! Przegapiłem ten ważny fakt (miesiące kalendarzowe są zerowe). Muszę ponownie przejrzeć to pytanie.
marcolopes
22

Istnieje wiele sposobów na znalezienie różnicy między datami i godzinami. Jednym z najprostszych znanych mi sposobów jest:

      Calendar calendar1 = Calendar.getInstance();
      Calendar calendar2 = Calendar.getInstance();
      calendar1.set(2012, 04, 02);
      calendar2.set(2012, 04, 04);
      long milsecs1= calendar1.getTimeInMillis();
      long milsecs2 = calendar2.getTimeInMillis();
      long diff = milsecs2 - milsecs1;
      long dsecs = diff / 1000;
      long dminutes = diff / (60 * 1000);
      long dhours = diff / (60 * 60 * 1000);
      long ddays = diff / (24 * 60 * 60 * 1000);

      System.out.println("Your Day Difference="+ddays);

Instrukcja print jest tylko przykładem - możesz ją sformatować w dowolny sposób.

JDGuide
źródło
5
@ manoj kumar bardhan: zapraszamy do przepełnienia stosu: Jak widzisz pytanie i odpowiedzi mają już lata. Twoja odpowiedź powinna dodać więcej do pytania / odpowiedzi niż istniejące.
Jayan
3
Co z dniami, które mają 23 lub 25 godzin z powodu przejścia na czas letni?
Joni
19

Ponieważ wszystkie odpowiedzi tutaj są poprawne, ale używają starszych bibliotek Java lub innych firm, takich jak Joda lub podobne, po prostu upuszczę inny sposób, używając nowych klas java.time w Javie 8 i nowszych. Zobacz samouczek Oracle .

Użyj LocalDatei ChronoUnit:

LocalDate d1 = LocalDate.of(2017, 5, 1);
LocalDate d2 = LocalDate.of(2017, 5, 18);

long days = ChronoUnit.DAYS.between(d1, d2);
System.out.println( days );
OcXocę 웃 Пepeúpa ツ
źródło
9

Odejmowanie daty w milisekundach prac (jak opisano w innym poście), ale trzeba mieć do korzystania HOUR_OF_DAY a nie godziny, kiedy czyszcząc części czasie swojego terminach:

public static final long MSPERDAY = 60 * 60 * 24 * 1000;
...
final Calendar dateStartCal = Calendar.getInstance();
dateStartCal.setTime(dateStart);
dateStartCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateStartCal.set(Calendar.MINUTE, 0);
dateStartCal.set(Calendar.SECOND, 0);
dateStartCal.set(Calendar.MILLISECOND, 0);
final Calendar dateEndCal = Calendar.getInstance();
dateEndCal.setTime(dateEnd);
dateEndCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateEndCal.set(Calendar.MINUTE, 0);
dateEndCal.set(Calendar.SECOND, 0);
dateEndCal.set(Calendar.MILLISECOND, 0);
final long dateDifferenceInDays = ( dateStartCal.getTimeInMillis()
                                  - dateEndCal.getTimeInMillis()
                                  ) / MSPERDAY;
if (dateDifferenceInDays > 15) {
    // Do something if difference > 15 days
}
Malcolm Boekhoff
źródło
1
To najlepsza metoda określenia różnicy między dwiema datami kalendarzowymi, bez wychodzenia poza standardowe biblioteki. Większość innych odpowiedzi traktuje dzień jako arbitralny 24-godzinny okres. Jeśli sekundy przestępne miałyby zostać odjęte, a nie dodane, ostateczne obliczenia mogłyby zostać wyłączone o jeden z powodu skrócenia, ponieważ różnica bezwzględna może być nieco mniejsza niż cała liczba wielokrotności MSPERDAY.
JulianSymes
8

Jeśli nie chcesz używać JodaTime lub podobnego, najlepszym rozwiązaniem jest prawdopodobnie:

final static long MILLIS_PER_DAY = 24 * 3600 * 1000;
long msDiff= date1.getTime() - date2.getTime();
long daysDiff = Math.round(msDiff / ((double)MILLIS_PER_DAY));

Liczba ms na dzień nie zawsze jest taka sama (z powodu czasu letniego i sekund przestępnych), ale jest bardzo bliska, a co najmniej odchylenia z powodu czasu letniego anulowane na dłuższe okresy. Dlatego dzielenie, a następnie zaokrąglanie da poprawny wynik (przynajmniej o ile lokalny kalendarz nie zawiera dziwnych skoków czasu innych niż czas letni i sekundy przestępne).

Należy pamiętać, że nadal zakłada to date1i date2są ustawione na tę samą porę dnia. Jak podkreśla Jon Skeet, dla różnych pór dnia musisz najpierw zdefiniować, co oznacza „różnica dat”.

Śleske
źródło
Problem jest w date1.getTime()vs date2.getTime(). Różnicując 2016-03-03 na 31.03.2016, obliczymy 27 dni, podczas gdy chcesz 28 dni.
Malat
@malat: Przepraszam, nie mogę śledzić. Właśnie tego spróbowałem - różnicowanie 2016-03-03 na 31.03.2016 z moim kodem oblicza 28 dni. Jeśli w twoim przykładzie jest to coś innego, rozważ zadanie tego jako osobnego pytania.
śleske
Czy również przeczytałeś moje zastrzeżenie? „nadal zakłada się, że data1 i data2 są ustawione na tę samą porę dnia”. Czy w teście wykorzystałeś różne pory dnia?
śleske
Ah, tak ! Brakowało mi sztuczki Math.round(), która wydaje się bezpieczna w „prawdziwym świecie” kalendarzy. Przepraszam za hałas.
Malat
8

Spójrz na Joda Time , który jest ulepszonym interfejsem API Data / Czas dla Javy i powinien działać dobrze ze Scalą.

Rob H.
źródło
zastąpiony przez java.time (JSR-310) w Javie 8
mal
5

Pokażę różnicę między Joda Interval a Days:

DateTime start = new DateTime(2012, 2, 6, 10, 44, 51, 0);
DateTime end = new DateTime(2012, 2, 6, 11, 39, 47, 1);
Interval interval = new Interval(start, end);
Period period = interval.toPeriod();
System.out.println(period.getYears() + " years, " + period.getMonths() + " months, " + period.getWeeks() + " weeks, " + period.getDays() + " days");
System.out.println(period.getHours() + " hours, " + period.getMinutes() + " minutes, " + period.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *1 weeks, 1 days*
//0 hours, 54 minutes, 56 seconds 

//Period can set PeriodType,such as PeriodType.yearMonthDay(),PeriodType.yearDayTime()...
Period p = new Period(start, end, PeriodType.yearMonthDayTime());
System.out.println(p.getYears() + " years, " + p.getMonths() + " months, " + p.getWeeks() + " weeks, " + p.getDays() + "days");
System.out.println(p.getHours() + " hours, " + p.getMinutes() + " minutes, " + p.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *0 weeks, 8 days*
//0 hours, 54 minutes, 56 seconds 
użytkownik674158
źródło
5

Jeśli potrzebujesz sformatowanego ciągu zwrotnego, takiego jak „2 dni 03h 42m 07s”, spróbuj tego:

public String fill2(int value)
{
    String ret = String.valueOf(value);

    if (ret.length() < 2)
        ret = "0" + ret;            
    return ret;
}

public String get_duration(Date date1, Date date2)
{                   
    TimeUnit timeUnit = TimeUnit.SECONDS;

    long diffInMilli = date2.getTime() - date1.getTime();
    long s = timeUnit.convert(diffInMilli, TimeUnit.MILLISECONDS);

    long days = s / (24 * 60 * 60);
    long rest = s - (days * 24 * 60 * 60);
    long hrs = rest / (60 * 60);
    long rest1 = rest - (hrs * 60 * 60);
    long min = rest1 / 60;      
    long sec = s % 60;

    String dates = "";
    if (days > 0) dates = days + " Days ";

    dates += fill2((int) hrs) + "h ";
    dates += fill2((int) min) + "m ";
    dates += fill2((int) sec) + "s ";

    return dates;
}
Ingo
źródło
2
Whoa, po co tworzyć własne, gdy Joda-Time zapewnia klasę Period, która jest już napisana i debugowana?
Basil Bourque
8
Dlaczego powinienem pobrać bibliotekę ze skompresowanym plikiem o rozmiarze 4,1 MB i dodać go do mojego projektu, gdy potrzebuję tylko 32 linii kodu?
Ingo
1
Ponieważ Joda-Time jest jak chipsy ziemniaczane: nie możesz zjeść tylko jednego. Wszędzie będziesz używać Joda-Time. A ponieważ Joda-Time jest dobrze przetestowanym i znoszonym kodem. A ponieważ Joda-Time analizuje i generuje standardowe ciągi czasu trwania ISO 8601, które mogą być przydatne do szeregowania lub raportowania tych wartości. A ponieważ Joda-Time zawiera formaterery do drukowania zlokalizowanych reprezentacji tych wartości.
Basil Bourque,
Nie cały kod jest testowany. stdpowyższe jest niezdefiniowane.
Basil Bourque,
@Basil Bourque: Dziękuję za podpowiedź. Poprawiłem źródło. Ten błąd pochodzi z tłumaczenia na angielski.
Ingo
5

Uwaga: data początkowa i końcowa to -> java.util.Date

import org.joda.time.Duration;
import org.joda.time.Interval;
// Use .getTime() unless it is a joda DateTime object
Interval interval = new Interval(startDate.getTime(), endDate.getTime());
Duration period = interval.toDuration();
//gives the number of days elapsed between start 
period.getStandardDays();

i data zakończenia

Podobnie jak w dni, możesz także uzyskać godziny, minuty i sekundy

period.getStandardHours();
period.getStandardMinutes();
period.getStandardSeconds();
Shravan Ramamurthy
źródło
4

Sprawdź przykład tutaj http://www.roseindia.net/java/beginners/DateDifferent.shtml Ten przykład pokazuje różnicę w dniach, godzinach, minutach, sekundach i milisekundach :).

import java.util.Calendar;
import java.util.Date;

public class DateDifferent {
    public static void main(String[] args) {
        Date date1 = new Date(2009, 01, 10);
        Date date2 = new Date(2009, 07, 01);
        Calendar calendar1 = Calendar.getInstance();
        Calendar calendar2 = Calendar.getInstance();
        calendar1.setTime(date1);
        calendar2.setTime(date2);
        long milliseconds1 = calendar1.getTimeInMillis();
        long milliseconds2 = calendar2.getTimeInMillis();
        long diff = milliseconds2 - milliseconds1;
        long diffSeconds = diff / 1000;
        long diffMinutes = diff / (60 * 1000);
        long diffHours = diff / (60 * 60 * 1000);
        long diffDays = diff / (24 * 60 * 60 * 1000);
        System.out.println("\nThe Date Different Example");
        System.out.println("Time in milliseconds: " + diff + " milliseconds.");
        System.out.println("Time in seconds: " + diffSeconds + " seconds.");
        System.out.println("Time in minutes: " + diffMinutes + " minutes.");
        System.out.println("Time in hours: " + diffHours + " hours.");
        System.out.println("Time in days: " + diffDays + " days.");
    }
}
YoK
źródło
3
-1 To źle, chyba że instancje daty zostały wyprowadzone z czasów UTC. Zobacz odpowiedź Jona Skeeta.
sleske
4

Użyj strefy czasowej GMT, aby uzyskać instancję kalendarza, ustaw czas za pomocą metody set klasy Calendar. Strefa czasowa GMT ma przesunięcie 0 (niezbyt ważne), a flaga czasu letniego ustawiona na wartość false.

    final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 9);
    cal.set(Calendar.DAY_OF_MONTH, 29);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date startDate = cal.getTime();

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 12);
    cal.set(Calendar.DAY_OF_MONTH, 21);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date endDate = cal.getTime();

    System.out.println((endDate.getTime() - startDate.getTime()) % (1000l * 60l * 60l * 24l));
Michał
źródło
Nie zapomnij wyzerować części milisekundowej, ponieważ jest to dobra odpowiedź w kontekście starych klas java.util.Dateitp. Użycie hacka do ustawienia strefy czasowej na GMT powoduje, że kod jest niewrażliwy na efekty czasu letniego.
Meno Hochschild,
4

Poniższy kod może dać pożądane wyniki:

String startDate = "Jan 01 2015";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
LocalDate date = LocalDate.parse(startDate, formatter);

String currentDate = "Feb 11 2015";
LocalDate date1 = LocalDate.parse(currentDate, formatter);

System.out.println(date1.toEpochDay() - date.toEpochDay());
Vishal Ganjare
źródło
3
Czy potrafisz dokładnie wyjaśnić, co robi Twój kod? Wydaje się, że pytanie dotyczy delty czasu, a na pierwszy rzut oka kod wydaje się zwracać różnicę dnia?
GHC
4
public static String getDifferenceBtwTime(Date dateTime) {

    long timeDifferenceMilliseconds = new Date().getTime() - dateTime.getTime();
    long diffSeconds = timeDifferenceMilliseconds / 1000;
    long diffMinutes = timeDifferenceMilliseconds / (60 * 1000);
    long diffHours = timeDifferenceMilliseconds / (60 * 60 * 1000);
    long diffDays = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24);
    long diffWeeks = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 7);
    long diffMonths = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 30.41666666));
    long diffYears = (long)(timeDifferenceMilliseconds / (1000 * 60 * 60 * 24 * 365));

    if (diffSeconds < 1) {
        return "one sec ago";
    } else if (diffMinutes < 1) {
        return diffSeconds + " seconds ago";
    } else if (diffHours < 1) {
        return diffMinutes + " minutes ago";
    } else if (diffDays < 1) {
        return diffHours + " hours ago";
    } else if (diffWeeks < 1) {
        return diffDays + " days ago";
    } else if (diffMonths < 1) {
        return diffWeeks + " weeks ago";
    } else if (diffYears < 12) {
        return diffMonths + " months ago";
    } else {
        return diffYears + " years ago";
    }
}   
Parth Solanki
źródło
3
int daysDiff = (date1.getTime() - date2.getTime()) / MILLIS_PER_DAY;
Bozho
źródło
5
-1 To źle, chyba że instancje daty zostały wyprowadzone z czasów UTC. Zobacz odpowiedź Jona Skeeta.
sleske
5
@sleske nie masz racji. Utracił już informacje o strefie czasowej Date, dlatego porównanie to jest najlepsze, jakie można osiągnąć w danych okolicznościach.
Bozho
1
OK, chyba oboje mamy rację. To prawda, że ​​zależy to od tego, skąd pochodzi instancja Date. Mimo to jest to tak powszechny błąd, że należy o nim wspomnieć.
sleske
Kiedy używam twojego rozwiązania, mówi: Niedopasowanie typu: nie można przekonwertować z długiego na int. Myślę, że powinieneś także dodać casting, czy czegoś mi brakuje?
Ad Infinitum
3

Najlepszą rzeczą do zrobienia jest

(Date1-Date2)/86 400 000 

Liczba ta jest liczbą milisekund w ciągu dnia.

Jedna data - druga data daje różnicę w milisekundach.

Zbierz odpowiedź w podwójnej zmiennej.

Revanth Kumar
źródło
Tak, czemu nie? Nie rozumiem w pewnym sensie pytania: OP ma różnicę w ms ... i jest pewna liczba ms w ciągu dnia (OK z pewnymi astronomicznymi korektami raz na niebieskim księżycu, ale OP nie robi nie mówią, że musi być tak precyzyjna, jak potrzebują tego astronomowie ...)
gryzoni Mike
3

Oto poprawne rozwiązanie Java 7 w O (1) bez żadnych zależności.

public static int countDaysBetween(Date date1, Date date2) {

    Calendar c1 = removeTime(from(date1));
    Calendar c2 = removeTime(from(date2));

    if (c1.get(YEAR) == c2.get(YEAR)) {

        return Math.abs(c1.get(DAY_OF_YEAR) - c2.get(DAY_OF_YEAR)) + 1;
    }
    // ensure c1 <= c2
    if (c1.get(YEAR) > c2.get(YEAR)) {
        Calendar c = c1;
        c1 = c2;
        c2 = c;
    }
    int y1 = c1.get(YEAR);
    int y2 = c2.get(YEAR);
    int d1 = c1.get(DAY_OF_YEAR);
    int d2 = c2.get(DAY_OF_YEAR);

    return d2 + ((y2 - y1) * 365) - d1 + countLeapYearsBetween(y1, y2) + 1;
}

private static int countLeapYearsBetween(int y1, int y2) {

    if (y1 < 1 || y2 < 1) {
        throw new IllegalArgumentException("Year must be > 0.");
    }
    // ensure y1 <= y2
    if (y1 > y2) {
        int i = y1;
        y1 = y2;
        y2 = i;
    }

    int diff = 0;

    int firstDivisibleBy4 = y1;
    if (firstDivisibleBy4 % 4 != 0) {
        firstDivisibleBy4 += 4 - (y1 % 4);
    }
    diff = y2 - firstDivisibleBy4 - 1;
    int divisibleBy4 = diff < 0 ? 0 : diff / 4 + 1;

    int firstDivisibleBy100 = y1;
    if (firstDivisibleBy100 % 100 != 0) {
        firstDivisibleBy100 += 100 - (firstDivisibleBy100 % 100);
    }
    diff = y2 - firstDivisibleBy100 - 1;
    int divisibleBy100 = diff < 0 ? 0 : diff / 100 + 1;

    int firstDivisibleBy400 = y1;
    if (firstDivisibleBy400 % 400 != 0) {
        firstDivisibleBy400 += 400 - (y1 % 400);
    }
    diff = y2 - firstDivisibleBy400 - 1;
    int divisibleBy400 = diff < 0 ? 0 : diff / 400 + 1;

    return divisibleBy4 - divisibleBy100 + divisibleBy400;
}


public static Calendar from(Date date) {

    Calendar c = Calendar.getInstance();
    c.setTime(date);

    return c;
}


public static Calendar removeTime(Calendar c) {

    c.set(HOUR_OF_DAY, 0);
    c.set(MINUTE, 0);
    c.set(SECOND, 0);
    c.set(MILLISECOND, 0);

    return c;
}
suxumuxu
źródło
2

Jest to prawdopodobnie najprostszy sposób na zrobienie tego - być może dlatego, że już od jakiegoś czasu programuję w Javie (z jej niewygodnymi bibliotekami daty i czasu), ale ten kod wydaje mi się „prosty i miły”!

Czy jesteś zadowolony z tego, że wynik jest zwracany w milisekundach, czy też jest częścią pytania, które wolisz, aby zwracano go w innym formacie?

Andrzej Doyle
źródło
2

Nie używa standardowego interfejsu API, nie. Możesz rzucić własny, robiąc coś takiego:

class Duration {
    private final TimeUnit unit;
    private final long length;
    // ...
}

Lub możesz użyć Jody :

DateTime a = ..., b = ...;
Duration d = new Duration(a, b);
gustafc
źródło
2

Aby odpowiedzieć na pierwsze pytanie:

Umieść następujący kod w funkcji takiej jak Long getAge () {}

Date dahora = new Date();
long MillisToYearsByDiv = 1000l *60l * 60l * 24l * 365l;
long javaOffsetInMillis = 1990l * MillisToYearsByDiv;
long realNowInMillis = dahora.getTime() + javaOffsetInMillis;
long realBirthDayInMillis = this.getFechaNac().getTime() + javaOffsetInMillis;
long ageInMillis = realNowInMillis - realBirthDayInMillis;

return ageInMillis / MillisToYearsByDiv;

Najważniejsze tutaj jest praca z długimi liczbami podczas mnożenia i dzielenia. I oczywiście przesunięcie, które Java stosuje w swoim rachunku dat.

:)

MugiWara No Carlos
źródło
2

Ponieważ pytanie jest oznaczone Scalą,

import scala.concurrent.duration._
val diff = (System.currentTimeMillis() - oldDate.getTime).milliseconds
val diffSeconds = diff.toSeconds
val diffMinutes = diff.toMinutes
val diffHours = diff.toHours
val diffDays = diff.toDays
VasiliNovikov
źródło
2

Po przejrzeniu wszystkich pozostałych odpowiedzi, aby zachować typ daty Java 7, ale być bardziej precyzyjnym / standardowym w przypadku podejścia różnicowego Java 8,

public static long daysBetweenDates(Date d1, Date d2) {
    Instant instant1 = d1.toInstant();
    Instant instant2 = d2.toInstant();
    long diff = ChronoUnit.DAYS.between(instant1, instant2);
    return diff;
}
gen b.
źródło
1

Spróbuj tego:

int epoch = (int) (new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse("01/01/1970  00:00:00").getTime() / 1000);

możesz edytować ciąg w param () param metod.

sabbibJAVA
źródło