Разница в днях между двумя датами в Java?


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

  int age=calculateDifference(agingDate, today);

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

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

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

EDIT:

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

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 времени библиотека.

20 79

20 ответов:

Я бы предложил вам использовать отличный Джода Времени библиотека вместо дефектной java.утиль.Свидание и друзья. Вы могли бы просто написать

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

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

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

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

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.");
  }

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

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

вот источник приведенной выше ссылки в случае, если ссылка идет вниз:

/** 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;  
}

java.время

в Java 8 и более поздних, используйте java.временные рамки (учебник).

Duration

The 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 перечисление. Обратите внимание, что методы расчета возвращают a long, а не int.

long days = ChronoUnit.DAYS.between( then, now );

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

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;
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;
    }
}

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

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,

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

вот очень разумно выглядящая реализация:

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 EDT 2009 = 1,236,571,200,000
Пн 23 Февраля 00: 00: 00 EST 2009 = 1,235,365,200,000
MillisPerWeek = 604,800,000
таким образом,
(Mar09 - Feb23) / MillisPerWeek =
1,206,000,000 / 604,800,000 = 1,994...

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

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

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; 
     }

посмотреть getFragmentInDays методы в этом классе Apache commons-lang DateUtils.

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

Я запустил около 30 тестовых случаев против него, это единственный метод, который обрабатывает фрагменты sub day time правильно.

пример: если вы проходите сейчас и сейчас + 1 миллисекунда, это все тот же день. Делать 1-1-13 23:59:59.098 to 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 для анализа кода на лету

вы говорите, что он "отлично работает в Автономной программе", но что вы получаете "необычные значения разницы", когда вы "включаете это в мою логику для чтения из отчета". Это означает, что ваш отчет имеет некоторые значения, для которых он не работает правильно, и ваша автономная программа не имеет этих значений. Вместо автономной программы я предлагаю тестовый случай. Напишите тестовый случай так же, как и автономную программу, подкласс из класса TestCase JUnit. Теперь вы можете запустить очень конкретный например, зная, какое значение вы ожидаете (и не давайте его сегодня для тестового значения, потому что сегодня меняется с течением времени). Если вы введете значения, которые вы использовали в Автономной программе, ваши тесты, вероятно, пройдут. Это здорово-вы хотите, чтобы эти дела продолжали работать. Теперь добавьте значение из вашего отчета, которое не работает правильно. Ваш новый тест, вероятно, не удастся. Выясните, почему он не работает, исправьте его и получите зеленый (все тесты проходят). Запустите свой отчет. Посмотрите, что еще сломано; напишите тест; пусть это пройдет. Довольно скоро вы обнаружите, что ваш отчет работает.

сто строк кода для этой основной функции???

простой способ:

protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
    return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24); 
    // MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}
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;
}

ThreeTen-Extra

The ответ Виталия Федоренко правильно, описывая, как выполнить этот расчет современным способом с java.время класса (Duration & ChronoUnit) встроенный в Java 8 и позже (и back-ported to Java 6 & 7 и для Android).

Days

если вы регулярно используете несколько дней в своем коде, вы можете заменить простые целые числа с использованием класса. Элемент Days класс можно найти в ThreeTen-Extra проект, расширение java.время и полигон для возможных будущих дополнений к Java.время. Элемент Days класс предоставляет типобезопасный способ представления количества дней в вашем приложении. Класс включает в себя удобные константы для ZERO и ONE.

учитывая старую устаревшую java.util.Date объекты в вопросе, сначала преобразуйте их в современные java.time.Instant объекты. Старые классы даты и времени имеют недавно добавленные методы для облегчения преобразования в java.время такое java.util.Date::toInstant.

Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant();  // Exclusive.

передать Instant объекты к заводскому методу для org.threeten.extra.Days.

в текущей реализации (2016-06) это обертка вызова java.time.temporal.ChronoUnit.DAYS.between, читать ChronoUnit класс doc для деталей. Чтобы было понятно: все прописные буквы DAYS в перечисление ChronoUnit в то время как начальная-cap Days это класс от ThreeTen-Extra.

Days days = Days.between( start , stop );

вы можете передать эти Days объекты вокруг вашего собственного кода. Вы можете сериализовать строку в стандартном ISO 8601 по телефону toString. Это формат PnD использует P отметить начало и D означает "дни", с несколькими днями между ними. Оба java.классы времени и ThreeTen-Extra используют эти стандартные форматы по умолчанию при создании и анализе строк, представляющих значения даты-времени.

String output = days.toString();

P3D

Days days = Days.parse( "P3D" );  

этот код вычисляет дни между 2 строками даты:

    static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
    static final String DATE_FORMAT = "dd-MM-yyyy";
    public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
    Date fromDate;
    Date toDate;
    fromDate = format.parse(fromDateStr);
    toDate = format.parse(toDateStr);
    return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}

если вы ищете решение, которое возвращает правильное количество или дней между, например,11/30/2014 23:59 и 12/01/2014 00:01 вот решение с использованием времени Джоды.

private int getDayDifference(long past, long current) {
    DateTime currentDate = new DateTime(current);
    DateTime pastDate = new DateTime(past);
    return currentDate.getDayOfYear() - pastDate.getDayOfYear();
} 

эта реализация вернет 1 как разница в днях. Большинство решений, опубликованных здесь, вычисляют разницу в миллисекундах между двумя датами. Это значит, что 0 будут возвращены, потому что есть только 2 минуты разница между этими двумя датами.

Я уже писал об этом. Это репост от вычисление разницы между двумя экземплярами даты Java.

public int getDiffernceInDays(long timeAfter, long timeBefore) {
    Calendar calendarAfter = Calendar.getInstance();
    calendarAfter.setTime(new Date(timeAfter));

    Calendar calendarNewAfter = Calendar.getInstance();
    calendarNewAfter.set(calendarAfter.get(Calendar.YEAR), calendarAfter.get(Calendar.MONTH), calendarAfter.get(Calendar.DAY_OF_MONTH));

    Calendar calendarBefore = Calendar.getInstance();
    calendarBefore.setTime(new Date(timeBefore));

    Calendar calendarNewBefore = Calendar.getInstance();
    calendarNewBefore.set(calendarBefore.get(Calendar.YEAR), calendarBefore.get(Calendar.MONTH), calendarBefore.get(Calendar.DAY_OF_MONTH));

    return (int) ((calendarNewAfter.getTime().getTime() - calendarNewBefore.getTime().getTime()) / (24 * 60 * 60 * 1000));
}

вы должны использовать библиотеку времени Joda, потому что Java Util Date иногда возвращает неправильные значения.

Joda vs Java Util Date

например дни между вчерашним днем (dd-mm-yyyy, 12-07-2016) и первым днем года в 1957 году (dd-mm-yyyy, 01-01-1957):

public class Main {

public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date date = null;
    try {
        date = format.parse("12-07-2016");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    //Try with Joda - prints 21742
    System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
    //Try with Java util - prints 21741
    System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));    
}


private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
    DateTime jodaDateTime = new DateTime(date);
    DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
    DateTime y1957 = formatter.parseDateTime("01-01-1957");

    return Days.daysBetween(y1957 , jodaDateTime).getDays();
}

private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date y1957 = null;
    try {
        y1957 = format.parse("01-01-1957");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}

поэтому я действительно советую вам использовать библиотеку времени Joda.

Я сделал это таким образом. это просто :)

Date d1 = jDateChooserFrom.getDate();
Date d2 = jDateChooserTo.getDate();

Calendar day1 = Calendar.getInstance();
day1.setTime(d1);

Calendar day2 = Calendar.getInstance();
day2.setTime(d2);

int from = day1.get(Calendar.DAY_OF_YEAR);
int to = day2.get(Calendar.DAY_OF_YEAR);

int difference = to-from;