Подтвердить что ты не робот

Как получить количество дней между двумя экземплярами календаря?

Я хочу найти разницу между двумя объектами Calendar в количестве дней, если есть изменение даты, например, если часы, отмеченные с 23: 59-0: 00, должны иметь разницу в день.

Я написал это

public static int daysBetween(Calendar startDate, Calendar endDate) {  
    return Math.abs(startDate.get(Calendar.DAY_OF_MONTH)-endDate.get(Calendar.DAY_OF_MONTH));  
} 

но он не работает, поскольку он только дает разницу между днями, если разница в месяце равна нулю.

4b9b3361

Ответ 1

В Java 8 и более поздних версиях мы могли бы просто использовать классы java.time.

hoursBetween = ChronoUnit.HOURS.between(calendarObj.toInstant(), calendarObj.toInstant());

daysBetween = ChronoUnit.DAYS.between(calendarObj.toInstant(), calendarObj.toInstant());

Ответ 2

Попробуйте выполнить следующий подход:

public static long daysBetween(Calendar startDate, Calendar endDate) {
    long end = endDate.getTimeInMillis();
    long start = startDate.getTimeInMillis();
    return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}

Ответ 3

Эта функция вычисляет количество дней между двумя календарями как количество календарных дней месяца, которые находятся между ними, что и требовался OP. Расчет выполняется путем подсчета количества кратных 86 400 000 миллисекунд между календарями после того, как оба они установлены до полуночи их соответствующих дней.

Например, моя функция будет вычислять разницу в 1 день между Календарем с 1 января, 23:59 и 2 января, 12:01.

import java.util.concurrent.TimeUnit;

/**
 * Compute the number of calendar days between two Calendar objects. 
 * The desired value is the number of days of the month between the
 * two Calendars, not the number of milliseconds' worth of days.
 * @param startCal The earlier calendar
 * @param endCal The later calendar
 * @return the number of calendar days of the month between startCal and endCal
 */
public static long calendarDaysBetween(Calendar startCal, Calendar endCal) {

    // Create copies so we don't update the original calendars.

    Calendar start = Calendar.getInstance();
    start.setTimeZone(startCal.getTimeZone());
    start.setTimeInMillis(startCal.getTimeInMillis());

    Calendar end = Calendar.getInstance();
    end.setTimeZone(endCal.getTimeZone());
    end.setTimeInMillis(endCal.getTimeInMillis());

    // Set the copies to be at midnight, but keep the day information.

    start.set(Calendar.HOUR_OF_DAY, 0);
    start.set(Calendar.MINUTE, 0);
    start.set(Calendar.SECOND, 0);
    start.set(Calendar.MILLISECOND, 0);

    end.set(Calendar.HOUR_OF_DAY, 0);
    end.set(Calendar.MINUTE, 0);
    end.set(Calendar.SECOND, 0);
    end.set(Calendar.MILLISECOND, 0);

    // At this point, each calendar is set to midnight on 
    // their respective days. Now use TimeUnit.MILLISECONDS to
    // compute the number of full days between the two of them.

    return TimeUnit.MILLISECONDS.toDays(
            Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
}

Ответ 4

ОБНОВЛЕНИЕ Проект Joda-Time, который теперь находится в режиме обслуживания, рекомендует выполнить переход на классы java.time. См. Ответ от Anees A для расчета прошедших часов и см. мой новый ответ для использования java.time для расчета прошедших дней с учетом календаря.

Joda времени

Старые классы java.util.Date/.Calendar общеизвестно хлопотны и их следует избегать.

Вместо этого используйте библиотеку Joda-Time. Если у вас нет технологии Java 8, в этом случае используйте ее преемник - встроенную инфраструктуру java.time (не в Android по состоянию на 2015 г.).

Поскольку вас интересуют только "дни", определенные как даты (а не 24-часовые периоды), давайте сосредоточимся на датах. Joda-Time предлагает класс LocalDate для представления значения только для даты без времени суток и часового пояса.

Несмотря на отсутствие часового пояса, обратите внимание, что часовой пояс имеет решающее значение при определении даты, такой как "сегодня". Новый день наступает раньше на востоке, чем на западе. Таким образом, дата не одинакова во всем мире в один момент, дата зависит от вашего часового пояса.

DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
LocalDate today = LocalDate.now ( zone );

Давайте посчитаем количество дней до следующей недели, которое, конечно, должно быть семь.

LocalDate weekLater = today.plusWeeks ( 1 );
int elapsed = Days.daysBetween ( today , weekLater ).getDays ();

getDays на конце извлекает простое число int из объекта Days, возвращенного daysBetween.

Дамп на консоль.

System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );

today: 2015-12-22 to weekLater: 2015-12-29 is days: 7

У вас есть объекты календаря. Нам нужно преобразовать их в объекты Joda-Time. Внутренне объекты Календаря имеют целое число long, отслеживающее количество миллисекунд с начала первого момента 1970 года в UTC. Мы можем извлечь это число и передать его Joda-Time. Нам также нужно назначить желаемый часовой пояс, по которому мы собираемся определить дату.

long startMillis = myStartCalendar.getTimeInMillis();
DateTime startDateTime = new DateTime( startMillis , zone );

long stopMillis = myStopCalendar.getTimeInMillis();
DateTime stopDateTime = new DateTime( stopMillis , zone );

Преобразование объектов DateTime в LocalDate.

LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();

Теперь сделайте то же самое вычисление, которое мы видели ранее.

int elapsed = Days.daysBetween ( start , stop ).getDays ();

Ответ 5

Расширение @JK1 отличный ответ:

public static long daysBetween(Calendar startDate, Calendar endDate) {

    //Make sure we don't change the parameter passed
    Calendar newStart = Calendar.getInstance();
    newStart.setTimeInMillis(startDate.getTimeInMillis());
    newStart.set(Calendar.HOUR_OF_DAY, 0)
    newStart.set(Calendar.MINUTE, 0)
    newStart.set(Calendar.SECOND, 0)
    newStart.set(Calendar.MILLISECOND, 0)

    Calendar newEnd = Calendar.getInstance();
    newEnd.setTimeInMillis(endDate.getTimeInMillis());
    newEnd.set(Calendar.HOUR_OF_DAY, 0)
    newEnd.set(Calendar.MINUTE, 0)
    newEnd.set(Calendar.SECOND, 0)
    newEnd.set(Calendar.MILLISECOND, 0)

    long end = newEnd.getTimeInMillis();
    long start = newStart.getTimeInMillis();
    return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}

Ответ 6

java.time

Ответ мохамеда Аниса правильный для часов, но неправильный для дней. Для подсчета дней требуется часовой пояс. Этот другой ответ использует Instant, который является моментом в UTC, всегда в UTC. Таким образом, вы не получаете правильное количество прошедших календарных дней.

Чтобы подсчитать дни по календарю, преобразуйте свое прежнее Calendar в ZonedDateTime, а затем укажите ChronoUnit.DAYS.between.

Часовой пояс

Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париже Франция - это новый день, который все еще "вчера" в Монреале-Квебеке.

Если часовой пояс не указан, JVM неявно применяет свой текущий часовой пояс по умолчанию. Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться. Лучше явно указать желаемый/ожидаемый часовой пояс в качестве аргумента. Если важно, подтвердите зону с вашим пользователем.

Укажите правильное имя часового пояса в формате Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 2-4-буквенное сокращение, такое как EST или IST, поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;           // Capture the current date as seen through the wall-clock time used by the people of a certain region (a time zone).

Если вы хотите использовать текущий часовой пояс по умолчанию для JVM, запросите его и передайте в качестве аргумента. Если опущен, код становится неоднозначным для чтения, поскольку мы точно не знаем, намеревались ли вы использовать значение по умолчанию или если вы, как и многие программисты, не знали об этой проблеме.

ZoneId z = ZoneId.systemDefault() ;              // Get JVMs current default time zone.

Конвертировать из GregorianCalendar в ZonedDateTime

Ужасный GregorianCalendar, вероятно, конкретный класс, стоящий за вашим Calendar. Если это так, перейдите из этого унаследованного класса в современный, ZonedDateTime.

GregorianCalendar gc = null ;                    // Legacy class representing a moment in a time zone. Avoid this class as it is terribly designed.
if( myCal instanceof GregorianCalendar ) {       // See if your 'Calendar' is backed by a 'GregorianCalendar' class.
    gc = (GregorianCalendar) myCal ;             // Cast from the more general class to the concrete class.
    ZonedDateTime zdt = gc.toZonedDateTime() ;   // Convert from legacy class to modern class.
}

Полученный объект ZonedDateTime содержит объект ZoneId для часового пояса. После установки этой зоны вы можете рассчитать прошедшие календарные дни.

Рассчитать прошедшие дни

Чтобы рассчитать истекшее время в терминах лет-месяцев-дней, используйте класс Period.

Period p = Period.between( zdtStart , zdtStop ) ;

Если вы хотите, чтобы общее количество дней прошло, используйтеe ChronoUnit.

 long days = ChronoUnit.DAYS.between( zdtStart , zdtStop ) ;

Table of types of date-time classes in modern java.time versus legacy


О java.time

Среда java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Чтобы узнать больше, см. Учебное пособие по Oracle. И поищите в Кару множество примеров и объяснений. Спецификация: JSR 310.

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

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

Где взять классы java.time?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11 и более поздние версии - часть стандартного Java API с встроенной реализацией ,
    • Java 9 добавляет некоторые мелкие функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 & 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и еще more.

Ответ 7

Вот мое решение, использующее старые добрые объекты Календаря:

public static int daysApart(Calendar d0,Calendar d1)
{
    int days=d0.get(Calendar.DAY_OF_YEAR)-d1.get(Calendar.DAY_OF_YEAR);
    Calendar d1p=Calendar.getInstance();
    d1p.setTime(d1.getTime());
    for (;d1p.get(Calendar.YEAR)<d0.get(Calendar.YEAR);d1p.add(Calendar.YEAR,1))
    {
        days+=d1p.getActualMaximum(Calendar.DAY_OF_YEAR);
    }
    return days;
}

Это предполагает, что d0 позже, чем d1. Если это не гарантировано, вы всегда можете проверить и поменять их местами.

Основной принцип состоит в том, чтобы принимать разницу между днями года каждого. Если бы они были в одном и том же году, это было бы так.

Но они могут быть разные годы. Таким образом, я перебираю все годы между ними, добавляя количество дней в году. Обратите внимание, что getActualMaximum возвращает 366 в високосные годы и 365 в не високосные годы. Вот почему нам нужен цикл, вы не можете просто умножить разницу между годами на 365, потому что там может быть високосный год. (Мой первый черновик использовал getMaximum, но он не работает, потому что он возвращает 366 независимо от года. GetMaximum - это максимум для ЛЮБОГО года, а не для этого конкретного года.)

Поскольку в этом коде не делается никаких предположений о количестве часов в дне, он не обманывается летним временем.

Ответ 8

У меня есть аналогичный (не точный) подход, приведенный выше fooobar.com/questions/249744/....

И написали тестовые примеры вокруг api, для меня это провалилось, если я прошел 8 марта 2017 года - в качестве даты начала и 8 апреля 2017 года в качестве даты окончания.

Есть несколько дат, где вы увидите разницу на 1 день. Поэтому я сделал некоторые небольшие изменения в моем api, и мой текущий api теперь выглядит примерно так.

   public long getDays(long currentTime, long endDateTime) {

Calendar endDateCalendar;
Calendar currentDayCalendar;


//expiration day
endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
endDateCalendar.setTimeInMillis(endDateTime);
endDateCalendar.set(Calendar.MILLISECOND, 0);
endDateCalendar.set(Calendar.MINUTE, 0);
endDateCalendar.set(Calendar.HOUR, 0);
endDateCalendar.set(Calendar.HOUR_OF_DAY, 0);

//current day
currentDayCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
currentDayCalendar.setTimeInMillis(currentTime);
currentDayCalendar.set(Calendar.MILLISECOND, 0);
currentDayCalendar.set(Calendar.MINUTE, 0);
currentDayCalendar.set(Calendar.HOUR,0);
currentDayCalendar.set(Calendar.HOUR_OF_DAY, 0);


long remainingDays = (long)Math.ceil((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));

return remainingDays;}

Я не использую TimeUnit.MILLISECONDS.toDays, которые вызывали у меня некоторые проблемы.

Ответ 9

Решение Kotlin, чисто полагается на календарь. В конце дает точное количество дней разницы. Вдохновленный @Jk1

 private fun daysBetween(startDate: Calendar, endDate: Calendar): Long {
        val start = Calendar.getInstance().apply {
            timeInMillis = 0
            set(Calendar.DAY_OF_YEAR, startDate.get(Calendar.DAY_OF_YEAR))
            set(Calendar.YEAR, startDate.get(Calendar.YEAR))
        }.timeInMillis
        val end = Calendar.getInstance().apply {
            timeInMillis = 0
            set(Calendar.DAY_OF_YEAR, endDate.get(Calendar.DAY_OF_YEAR))
            set(Calendar.YEAR, endDate.get(Calendar.YEAR))
        }.timeInMillis
        val differenceMillis = end - start
        return TimeUnit.MILLISECONDS.toDays(differenceMillis)
    }

Ответ 10

Если ваш проект не поддерживает новые классы Java 8 (как выбранный ответ), вы можете добавить этот метод для расчета дней без влияния часовых поясов или других фактов.

Это не так быстро (более сложная по времени), как другие методы, но это надежно, в любом случае сравнение дат редко превышает сотни или тысячи лет.

(Котлин)

/**
     * Returns the number of DAYS between two dates. Days are counted as calendar days
     * so that tomorrow (from today date reference) will be 1 ,  the day after 2 and so on
     * independent on the hour of the day.
     *
     * @param date - reference date, normally Today
     * @param selectedDate - date on the future
     */
fun getDaysBetween(date: Date, selectedDate: Date): Int {

            val d = initCalendar(date)
            val s = initCalendar(selectedDate)
            val yd = d.get(Calendar.YEAR)
            val ys = s.get(Calendar.YEAR)

            if (ys == yd) {
                return s.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
            }

            //greater year
            if (ys > yd) {
                val endOfYear = Calendar.getInstance()
                endOfYear.set(yd, Calendar.DECEMBER, 31)
                var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
                while (endOfYear.get(Calendar.YEAR) < s.get(Calendar.YEAR)-1) {
                    endOfYear.add(Calendar.YEAR, 1)
                    daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
                }
                return daysToFinish + s.get(Calendar.DAY_OF_YEAR)
            }

            //past year
            else {
                val endOfYear = Calendar.getInstance()
                endOfYear.set(ys, Calendar.DECEMBER, 31)
                var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - s.get(Calendar.DAY_OF_YEAR)
                while (endOfYear.get(Calendar.YEAR) < d.get(Calendar.YEAR)-1) {
                    endOfYear.add(Calendar.YEAR, 1)
                    daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
                }
                return daysToFinish + d.get(Calendar.DAY_OF_YEAR)
            }
        }

Модульные тесты, вы можете улучшить их. Мне не нужны были отрицательные дни, поэтому я не так много тестировал:

@Test
    fun 'Test days between on today and following days'() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26)

        future.set(2019, Calendar.AUGUST, 26)
        Assert.assertEquals(0, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.AUGUST, 27)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.SEPTEMBER, 1)
        Assert.assertEquals(6, manager.getDaysBetween(calendar.time, future.time))

        future.set(2020, Calendar.AUGUST, 26)
        Assert.assertEquals(366, manager.getDaysBetween(calendar.time, future.time)) //leap year

        future.set(2022, Calendar.AUGUST, 26)
        Assert.assertEquals(1096, manager.getDaysBetween(calendar.time, future.time))

        calendar.set(2019, Calendar.DECEMBER, 31)
        future.set(2020, Calendar.JANUARY, 1)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun 'Test days between on previous days'() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26)

        future.set(2019,Calendar.AUGUST,25)
        Assert.assertEquals(-1, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun 'Test days between hour does not matter'() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26,9,31,15)

        future.set(2019,Calendar.AUGUST,28, 7,0,0)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019,Calendar.AUGUST,28, 9,31,15)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019,Calendar.AUGUST,28, 23,59,59)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun 'Test days between with time saving change'() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.OCTOBER, 28)

        future.set(2019, Calendar.OCTOBER,29)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.OCTOBER,30)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
    }

Ответ 11

Календарь day1 = Calendar.getInstance();

Календарь day2 = Calendar.getInstance();

int diff = day1.get(Calendar.DAY_OF_YEAR) - day2.get(Calendar.DAY_OF_YEAR);