Количество дней между двумя датами в Joda-Time

336

Как найти разницу в днях между двумя экземплярами Joda-Time DateTime ? Под «разницей в днях» я подразумеваю, если начало в понедельник, а конец во вторник, я ожидаю, что возвращаемое значение равно 1 независимо от часа / минуты / секунд дат начала и окончания.

Days.daysBetween(start, end).getDays() дает мне 0, если начало вечером и конец утром.

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

Другими словами, месяцы с февраля по 4 марта также будут равны 1, как и часы с 14:45 до 15:12. Однако разница часов между 14:01 и 14:55 будет равна 0.

pvgoddijn
источник

Ответы:

393

Досадно, что ответ withTimeAtStartOfDay неправильный, но только изредка. Вы хотите:

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

Оказывается, что «полночь / начало дня» иногда означает 1:00 (переход на летнее время происходит таким образом в некоторых местах), который Days.daysBetween не обрабатывает должным образом.

// 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 дня. Есть ли способ добиться этого?
Суджит Джоши
@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() 
Bozho
источник
Сэр, со ссылкой на этот пост метод 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

К вашему сведению, проект 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 Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация 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:00. Я обновил его, чтобы использовать правильный ответ, который вы дали ниже, используя toLocalDate.
Алиса Перселл,
2

Принятый ответ создает два LocalDateобъекта, которые довольно дороги, если вы читаете много данных. Я использую это:

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

При вызове getMillis()вы используете уже существующие переменные.
MILLISECONDS.toDays()затем, используя простой арифметический расчет, не создает никакого объекта.

JBoy
источник
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);
AzizSM
источник
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;
}
user1091978
источник
1
вопрос специально ищет йода-тайм.
Капад