Разница в днях между двумя датами в Java? - PullRequest
80 голосов
/ 21 июля 2010

Мне нужно найти количество дней между двумя датами : одно из отчета, а другое текущая дата. Мой фрагмент:

  int age=calculateDifference(agingDate, today);

Здесь calculateDifference - это закрытый метод, agingDate и today - это Date объекты, просто для вашего пояснения. Я следил за двумя статьями на форуме Java: Тема 1 / Тема 2 .

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

Почему это происходит и как я могу это исправить?

РЕДАКТИРОВАТЬ:

Я получаю большее количество дней по сравнению с фактическим количеством дней.

public static int calculateDifference(Date a, Date b)
{
    int tempDifference = 0;
    int difference = 0;
    Calendar earlier = Calendar.getInstance();
    Calendar later = Calendar.getInstance();

    if (a.compareTo(b) < 0)
    {
        earlier.setTime(a);
        later.setTime(b);
    }
    else
    {
        earlier.setTime(b);
        later.setTime(a);
    }

    while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
    {
        tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
    {
        tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    return difference;
}

Примечание:

К сожалению, ни один из ответов не помог мне решить проблему. Я выполнил эту проблему с помощью библиотеки Joda-time .

Ответы [ 20 ]

148 голосов
/ 21 июля 2010

Я бы предложил вам использовать отличную библиотеку Joda Time вместо испорченной java.util.Date и друзей.Вы могли бы просто написать

import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;

Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th 
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34
48 голосов
/ 29 июля 2010

Я могу быть слишком поздно, чтобы присоединиться к игре, но какого черта, а?:)

Как вы думаете, это проблема с потоками?Как вы используете вывод этого метода, например?ИЛИ

Можем ли мы изменить ваш код, чтобы сделать что-то простое:

Calendar calendar1 = Calendar.getInstance();
    Calendar calendar2 = Calendar.getInstance();
    calendar1.set(<your earlier date>);
    calendar2.set(<your current date>);
    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.");
  }
23 голосов
/ 18 января 2012

diff / (24 * и т. Д.) Не учитывает часовой пояс, поэтому, если в вашем часовом поясе по умолчанию есть летнее время, вычисление может быть отменено.

Эта ссылка имеет приятную небольшую реализацию.

Вот источник вышеуказанной ссылки на случай, если ссылка отключится:

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  //assert: startDate must be before endDate  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  

и

/** Using Calendar - THE CORRECT (& Faster) WAY**/  
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
  //assert: startDate must be before endDate  
  int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;  
  long endInstant = endDate.getTimeInMillis();  
  int presumedDays = 
    (int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);  
  Calendar cursor = (Calendar) startDate.clone();  
  cursor.add(Calendar.DAY_OF_YEAR, presumedDays);  
  long instant = cursor.getTimeInMillis();  
  if (instant == endInstant)  
    return presumedDays;

  final int step = instant < endInstant ? 1 : -1;  
  do {  
    cursor.add(Calendar.DAY_OF_MONTH, step);  
    presumedDays += step;  
  } while (cursor.getTimeInMillis() != endInstant);  
  return presumedDays;  
}
16 голосов
/ 20 апреля 2014

java.time

В Java 8 и новее используйте java.time Framework ( Tutorial ).

Duration

Класс Duration представляет промежуток времени в виде количества секунд плюс дробная секунда.Он может считать дни, часы, минуты и секунды.

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());

ChronoUnit

Если все, что вам нужно, это количество дней, в качестве альтернативы вы можете использовать ChronoUnit enum .Обратите внимание, что методы вычисления возвращают long вместо int.

long days = ChronoUnit.DAYS.between( then, now );
13 голосов
/ 13 сентября 2012
import java.util.Calendar;
import java.util.Date;

public class Main {
    public static long calculateDays(String startDate, String endDate)
    {
        Date sDate = new Date(startDate);
        Date eDate = new Date(endDate);
        Calendar cal3 = Calendar.getInstance();
        cal3.setTime(sDate);
        Calendar cal4 = Calendar.getInstance();
        cal4.setTime(eDate);
        return daysBetween(cal3, cal4);
    }

    public static void main(String[] args) {
        System.out.println(calculateDays("2012/03/31", "2012/06/17"));

    }

    /** 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;
    }
}
13 голосов
/ 29 июля 2010

Это зависит от того, что вы определяете как разницу.Для сравнения двух дат в полночь вы можете сделать.

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;
9 голосов
/ 02 марта 2013

Решение с использованием разницы между временем в миллисекундах с правильным округлением для дат летнего времени:

public static long daysDiff(Date from, Date to) {
    return daysDiff(from.getTime(), to.getTime());
}

public static long daysDiff(long from, long to) {
    return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}

Одно примечание: Конечно, даты должны быть в некотором часовом поясе.

Важный код:

Math.round( (to - from) / 86400000D )

Если вы не хотите округлять, вы можете использовать даты UTC,

4 голосов
/ 28 июня 2011

Иллюстрация проблемы: (Мой код вычисляет дельту по неделям, но та же проблема относится к дельте по дням)

Вот очень разумная реализация:

public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;

static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
    long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
    int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
    return deltaInWeeks; 
}

Но этот тест не пройдёт:

public void testGetDeltaInWeeks() {
    delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
    assertEquals("weeks between Feb23 and Mar09", 2, delta);
}

Причина:

Пн Мар 09 00:00:00 ПО ВОСТОЧНОМУ ВРЕМЕНИ 2009 = 1 236 571 200 000
Пн 23 февраля 00:00:00 EST 2009 = 1 235 365 200 000
MillisPerWeek = 604 800 000
Таким образом,
(март 09 - 23 февраля) / MillisPerWeek =
1 206 000 000/604 800 000 = 1,994 ...

но любой, кто смотрит на календарь, согласится, что ответ 2.

3 голосов
/ 24 мая 2013

Я использую эту функцию:

DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days

моя функция:

import java.util.Date;

public long DATEDIFF(String date1, String date2) {
        long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
        long days = 0l;
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");

        Date dateIni = null;
        Date dateFin = null;        
        try {       
            dateIni = (Date) format.parse(date1);
            dateFin = (Date) format.parse(date2);
            days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;                        
        } catch (Exception e) {  e.printStackTrace();  }   

        return days; 
     }
2 голосов
/ 15 октября 2013

Основываясь на ответе @ Mad_Troll, я разработал этот метод.

Я провел около 30 тестов для проверки, это единственный метод, который правильно обрабатывает фрагменты дневного времени.

Пример: Если вы пройдете сейчас и сейчас + 1 миллисекунду, это все тот же день.Выполнение 1-1-13 23:59:59.098 до 1-1-13 23:59:59.099 возвращает 0 дней, правильно;другие методы, опубликованные здесь, не будут делать это правильно.

Стоит отметить, что не важно, каким образом вы их вводите. Если ваша дата окончания предшествует дате начала, она будет отсчитываться в обратном направлении.

/**
 * This is not quick but if only doing a few days backwards/forwards then it is very accurate.
 *
 * @param startDate from
 * @param endDate   to
 * @return day count between the two dates, this can be negative if startDate is after endDate
 */
public static long daysBetween(@NotNull final Calendar startDate, @NotNull final Calendar endDate) {

    //Forwards or backwards?
    final boolean forward = startDate.before(endDate);
    // Which direction are we going
    final int multiplier = forward ? 1 : -1;

    // The date we are going to move.
    final Calendar date = (Calendar) startDate.clone();

    // Result
    long daysBetween = 0;

    // Start at millis (then bump up until we go back a day)
    int fieldAccuracy = 4;
    int field;
    int dayBefore, dayAfter;
    while (forward && date.before(endDate) || !forward && endDate.before(date)) {
        // We start moving slowly if no change then we decrease accuracy.
        switch (fieldAccuracy) {
            case 4:
                field = Calendar.MILLISECOND;
                break;
            case 3:
                field = Calendar.SECOND;
                break;
            case 2:
                field = Calendar.MINUTE;
                break;
            case 1:
                field = Calendar.HOUR_OF_DAY;
                break;
            default:
            case 0:
                field = Calendar.DAY_OF_MONTH;
                break;
        }
        // Get the day before we move the time, Change, then get the day after.
        dayBefore = date.get(Calendar.DAY_OF_MONTH);
        date.add(field, multiplier);
        dayAfter = date.get(Calendar.DAY_OF_MONTH);

        // This shifts lining up the dates, one field at a time.
        if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
            fieldAccuracy--;
        // If day has changed after moving at any accuracy level we bump the day counter.
        if (dayBefore != dayAfter) {
            daysBetween += multiplier;
        }
    }
    return daysBetween;
}

Вы можете удалить аннотации @NotNull, которые используются Intellij для анализа кода на лету

...