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

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

  int age=calculateDifference(agingDate, today);

Здесь calculateDifference - частный метод, agingDate и today являются объектами Date, только для вашего разъяснения. Я следил за двумя статьями с форума Java, Thread 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-time библиотека.

Ответ 1

Я предлагаю вам использовать отличную 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

Ответ 2

Возможно, я слишком поздно присоединился к игре, но что, черт возьми?:)

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

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

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

Ответ 3

diff/(24 * и т.д.) не учитывает часовой пояс, поэтому, если в вашем часовом поясе по умолчанию есть 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;  
}

Ответ 4

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

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

Ответ 5

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

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;

Ответ 6

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

Ответ 7

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

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,

Ответ 8

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

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

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.

Ответ 9

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

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

Ответ 10

Посмотрите на методы getFragmentInDays в этом классе apost commons-lang DateUtils.

Ответ 11

На основе ответа @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 для анализа кода на лету

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

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

Ответ 15

ThreeTen-Extra

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

Days

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

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

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

Передайте оба объекта Instant методу factory для org.threeten.extra.Days.

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

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

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

String output = days.toString();

P3D

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

Ответ 16

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

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

Ответ 17

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

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 минуты.

Ответ 18

Я уже писал об этом. Это repost из Вычисление разницы между двумя датами даты 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));
}

Ответ 19

Вы должны использовать библиотеку времени 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.

Ответ 20

Я сделал это так. это легко:)

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;