Кількість днів між двома датами в Joda-Time


335

Як я можу знайти різницю в днях між двома екземплярами Joda-Time DateTime ? Маючи на увазі "різницю в днях", я маю на увазі, якщо старт - у понеділок, а кінець - у вівторок, я очікую значення повернення 1, незалежно від години / хвилини / секунди дати початку та кінця.

Days.daysBetween(start, end).getDays() дає мені 0, якщо початок вечора, а кінець вранці.

У мене є те саме питання з іншими полями дати, тому я сподівався, що буде загальний спосіб «ігнорувати» поля меншого значення.

Іншими словами, місяці між лютим та 4 березня також становитимуть 1, як і години між 14:45 та 15:12. Однак різниця в годині між 14:01 та 14:55 складе 0.

Відповіді:


393

Прикро, що відповідь withTimeAtStartOfDay помилкова, але лише зрідка. Ти хочеш:

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

Виявляється, що "півночі / початок дня" іноді означає 1 ранку (економія денного світла трапляється таким чином в деяких місцях), який Days.daysBet between не справляється належним чином.

// 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

Пройшовши по LocalDateстороні всю проблему.


Чи можете ви навести приклад з конкретними даними, якщо ви вважаєте, що Days.daysBetweenце неправильно?
Василь Бурк

Коли ти кажеш використовувати Instant, ти не просто говориш start.toInstant(), чи не так?
Патрік М

@PatrickM Так, я був. Поміркувавши, не ясно, які саме обмеження це має накласти, тому я вилучу це останнє речення. Дякую!
Еліс Перселл

@chrispy daysBetween док каже , що він повертає кількість ЦІЛИХ днів. У моєму випадку 2 дні та 1 година повинні повернути мені 3 дні. У вашому прикладі він повертається 2 дні. Чи є спосіб досягти цього?
Sujit Joshi

@SujitJoshi Боюся, що я не розумію твого питання. Я пропоную опублікувати повне запитання про переповнення стека та вставити тут посилання для мене :)
Аліса Перселл

186

Days Клас

Використання Daysкласу з withTimeAtStartOfDayметодом повинно працювати:

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

1
Дякую! Я намагався досягти поведінки android.text.format.DateUtils.getRelativeTimeSpanString () з joda, і це було дуже корисно.
gosho_ot_pochivka

1
Сер, з посиланням на цю посаду метод toDateMidnight()із типу DateTime застарілий.
Анікет Кулкарні

2
Тепер ви повинні використовувати .withTimeAtStartOfDay () замість .toDateMidnight ()
bgolson

2
що робити, якщо endдо start, повертає негативні дні?
ахій

7
@akhyar: чому б не спробувати?
Майкл Боргвардт

86

Ви можете використовувати LocalDate:

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

Сер, з посиланням на цю посаду метод toDateMidnight()із типу DateTime застарілий.
Анікет Кулкарні

Навіщо використовувати new LocalDate(date)більше date.toLocalDate()?
SARose

9

тл; д-р

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

… Або…

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

java.time

FYI, проект Joda-Time зараз перебуває в режимі обслуговування , команда радить перейти до класів java.time .

Еквівалент Joda-Time DateTime- це ZonedDateTime.

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

Мабуть, ви хочете рахувати дні за датами, тобто ви хочете ігнорувати час доби. Наприклад, починаючи хвилину до півночі і закінчуючи хвилину після півночі, це повинно призвести до одного дня. Для такої поведінки витягніть LocalDateзі свого ZonedDateTime. LocalDateКлас являє собою дату тільки значення без часу доби і без часового поясу.

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

Використовуйте ChronoUnitперерахунок, щоб обчислити минулі дні чи інші одиниці.

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

Обрізати

Що стосується того, щоб ви запитали про більш загальний спосіб зробити цей підрахунок там, де вас цікавить дельта годин як годинна година, а не повна година, як тривалість шістдесяти хвилин, скористайтеся truncatedToметодом.

Ось ваш приклад з 14:45 до 15:12 того ж дня.

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

Це не працює днями. Використовуйте toLocalDate () у цьому випадку.


Про java.time

Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.

Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .

Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .

Ви можете обмінюватися об'єктами java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або пізнішої версії. Немає потреби в струнах, немає потреби в java.sql.*заняттях.

Де отримати класи java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 та новіших версій
    • Вбудований.
    • Частина стандартного Java API з пакетною реалізацією.
    • Java 9 додає деякі незначні функції та виправлення.
  • Java SE 6 та Java SE 7
    • Значна частина функцій java.time підтримується на Java 6 і 7 у ThreeTen-Backport .
  • Android
    • Пізніші версії реалізації пакетів Android для класів java.time.
    • Для більш ранньої Android (<26), то ThreeTenABP проект адаптує ThreeTen-Backport (згаданий вище). Див. Як користуватися ThreeTenABP… .

Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є передумовою для можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval, YearWeek, YearQuarter, і більш .

Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є передумовою для можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval, YearWeek, YearQuarter, і більш .


Ваш TL; DR протягом днів, що використовують укорочений, стає здобиччю помилки, детально описаної у прийнятій відповіді, а саме він є відключеним, коли початок дня о 1 годині ночі. Я оновив його, щоб використовувати правильну відповідь, яку ви надали далі, використовуючи toLocalDate.
Аліса Перселл

2

Прийнята відповідь створює два LocalDateоб'єкти, які є досить дорогими, якщо ви читаєте багато даних. Я використовую це:

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

Викликаючи getMillis()ви використовуєте вже існуючі змінні.
MILLISECONDS.toDays()то, використовуючи простий арифметичний обчислення, не створює жодного об'єкта.


1
Ця відповідь працює лише в тому випадку, якщо ваше визначення "днів" триває рівно 24 години, без огляду на часові пояси та дати. Якщо так, використовуйте такий підхід. Якщо ні, перегляньте інші відповіді, які стосуються часових поясів.
Василь Бурк

@BasilBourque, ти маєш рацію, все-таки я б пішов на рішення, засноване на арифметичному обчисленні, а не будував дорогі об'єкти для кожного виклику методу, є багато ароматів для таких розрахунків, якщо ти читаєш дані з веб-сторінки, це може бути нормально, але якщо ви обробляєте файл журналу із сотнями тисяч рядків, це робиться дуже повільно
JBoy

1
Java оптимізує розподіл об'єктів, якщо ваш цикл гарячий. Дивіться docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Еліс Перселл

1

java.time.Period

Використовуйте java.time.Periodклас для підрахунку днів .

Оскільки Java 8 для обчислення різниці є більш інтуїтивно зрозумілим LocalDate, LocalDateTimeщоб представити дві дати

    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
питання спеціально шукає час joda.
капад
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.