Liczba dni między dwiema datami w Joda-Time


335

Jak znaleźć różnicę w dniach między dwiema instancjami Joda-Time DateTime ? Z „różnicą dni” mam na myśli, że jeśli początek jest w poniedziałek, a koniec we wtorek, spodziewam się zwrotu 1, niezależnie od godziny / minuty / sekund daty rozpoczęcia i zakończenia.

Days.daysBetween(start, end).getDays() daje mi 0, jeśli początek jest wieczorem i kończy się rano.

Mam również ten sam problem z innymi polami dat, więc miałem nadzieję, że będzie ogólny sposób na „zignorowanie” pól o mniejszym znaczeniu.

Innymi słowy, miesiące między lutym a 4 marca będą również wynosić 1, podobnie jak godziny między 14:45 a 15:12. Jednak różnica godzin między 14:01 a 14:55 wynosiłaby 0.

Odpowiedzi:


393

Irytujące jest to, że odpowiedź withTimeAtStartOfDay jest błędna, ale tylko sporadycznie. Chcesz:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

Okazuje się, że „północ / początek dnia” czasami oznacza 1 rano (w niektórych miejscach w ten sposób można zaoszczędzić światło dzienne), co Days.daysBetween nie obsługuje poprawnie.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Przechodzenie przez LocalDateboczną stronę całego problemu.


Czy możesz podać przykładowy przypadek z konkretnymi danymi, które Twoim zdaniem Days.daysBetweensą nieprawidłowe?
Basil Bourque,

Kiedy mówisz używać Instant, nie mówisz tylko start.toInstant(), prawda?
Patrick M,

@PatrickM Tak, byłem. Po zastanowieniu nie jest jasne, jakie ograniczenia ma to nałożyć, więc usunę to ostatnie zdanie. Dzięki!
Alice Purcell,

@chrispy daysBetween doc mówi, że zwraca liczbę całością dni. W moim przypadku 2 dni i 1 godzina powinny zwrócić mi 3 dni. W twoim przykładzie zwraca 2 dni. Czy istnieje sposób na osiągnięcie tego?
Sujit Joshi

@SujitJoshi Obawiam się, że nie rozumiem twojego pytania. Sugeruję opublikowanie pełnego pytania dotyczącego przepełnienia stosu i wklejenie tutaj linku :)
Alice Purcell,

186

Days Klasa

Używanie Daysklasy z withTimeAtStartOfDaymetodą powinno działać:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

1
dzięki! Próbowałem osiągnąć zachowanie android.text.format.DateUtils.getRelativeTimeSpanString () za pomocą jody i było to naprawdę przydatne.
gosho_ot_pochivka

1
Sir, w odniesieniu do tego postu metoda toDateMidnight()typu DateTime jest przestarzała.
Aniket Kulkarni

2
Powinieneś teraz użyć .withTimeAtStartOfDay () zamiast .toDateMidnight ()
bgolson

2
co jeśli endjest przed start, czy zwraca dni negatywne?
akhy

7
@akhyar: dlaczego tego nie spróbujesz?
Michael Borgwardt

86

możesz użyć LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

Sir, w odniesieniu do tego postu metoda toDateMidnight()typu DateTime jest przestarzała.
Aniket Kulkarni

Dlaczego warto korzystać z new LocalDate(date)ponad date.toLocalDate()?
SARose

9

tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…lub…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

Do Twojej wiadomości, projekt Joda-Time jest teraz w trybie konserwacji , a zespół doradza migrację do klas java.time .

Odpowiednikiem Joda-Time DateTimejest ZonedDateTime.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Najwyraźniej chcesz policzyć dni według dat, co oznacza, że ​​chcesz zignorować porę dnia. Na przykład rozpoczęcie minuty przed północą i zakończenie minuty po północy powinno dać jeden dzień. W przypadku tego zachowania wyodrębnij LocalDatez pliku ZonedDateTime. LocalDateKlasa reprezentuje wartość data-tylko bez time-of-day i bez strefy czasowej.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Użyj ChronoUnitwyliczenia, aby obliczyć upływające dni lub inne jednostki.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Ścięty

Jeśli pytasz o bardziej ogólny sposób liczenia, w którym interesuje Cię delta godzin jako godziny, a nie pełnych godzin jako przedziałów czasu wynoszących sześćdziesiąt minut, skorzystaj z tej truncatedTometody.

Oto twój przykład z 14:45 do 15:12 tego samego dnia.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

To nie działa przez kilka dni. W tym przypadku użyj funkcji toLocalDate ().


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, i SimpleDateFormat.

Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do klas 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 .

Możesz wymieniać obiekty java.time bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie potrzeba ciągów, nie ma potrzeby java.sql.*klas.

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 .

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 .


Twój TL; DR na kilka dni przy użyciu obcinania pada ofiarą błędu wyszczególnionego w zaakceptowanej odpowiedzi, a mianowicie jest wyłączany, gdy początek dnia jest o 1 w nocy. Zaktualizowałem go, aby używał poprawnej odpowiedzi, którą podałeś dalej, używając toLocalDate.
Alice Purcell,

2

Zaakceptowana odpowiedź tworzy dwa LocalDateobiekty, które są dość drogie, jeśli czytasz dużo danych. Używam tego:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

Dzwoniąc getMillis()używasz już istniejących zmiennych.
MILLISECONDS.toDays()następnie wykorzystuje proste obliczenia arytmetyczne, nie tworzy żadnego obiektu.


1
Ta odpowiedź działa tylko wtedy, gdy twoja definicja „dni” ma dokładnie 24 godziny, bez względu na strefy czasowe i daty. Jeśli tak, skorzystaj z tego podejścia. Jeśli nie, spójrz na inne odpowiedzi dotyczące stref czasowych.
Basil Bourque,

@BasilBourque, masz rację, nadal wybrałbym rozwiązanie oparte na obliczeniach arytmetycznych zamiast budowania drogich obiektów dla każdego wywołania metody, istnieje wiele możliwości takich obliczeń, jeśli czytasz dane wejściowe ze strony internetowej, może być w porządku, ale jeśli przetwarzasz plik dziennika zawierający setki tysięcy wierszy, powoduje to, że jest bardzo powolny
JBoy

1
Java zoptymalizuje alokację obiektów, jeśli twoja pętla jest gorąca. Zobacz docs.oracle.com/javase/7/docs/technotes/guides/vm/...
Alice Purcell,

1

java.time.Period

Użyj java.time.Periodklasy, by policzyć dni .

Ponieważ Java 8 obliczanie różnicy jest bardziej intuicyjne w użyciu LocalDate, LocalDateTimedo przedstawienia dwóch dat

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);

0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    

-11
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

1
pytanie dotyczy konkretnie czasu joda.
kapad
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.