Вычисление разницы между двумя экземплярами даты Java - PullRequest
396 голосов
/ 12 октября 2009

Я использую класс Java java.util.Date в Scala и хочу сравнить объект Date и текущее время. Я знаю, что могу вычислить дельту с помощью getTime ():

(new java.util.Date()).getTime() - oldDate.getTime()

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

Ответы [ 45 ]

503 голосов
/ 18 мая 2012

Простой diff (без lib)

/**
 * Get a diff between two dates
 * @param date1 the oldest date
 * @param date2 the newest date
 * @param timeUnit the unit in which you want the diff
 * @return the diff value, in the provided unit
 */
public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) {
    long diffInMillies = date2.getTime() - date1.getTime();
    return timeUnit.convert(diffInMillies,TimeUnit.MILLISECONDS);
}

И тогда вы можете позвонить:

getDateDiff(date1,date2,TimeUnit.MINUTES);

, чтобы получить разницу в 2 даты в минутах.

TimeUnit - это java.util.concurrent.TimeUnit, стандартное перечисление Java от наноса до дней.


человекочитаемый diff (без lib)

public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {

    long diffInMillies = date2.getTime() - date1.getTime();

    //create the list
    List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
    Collections.reverse(units);

    //create the result map of TimeUnit and difference
    Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
    long milliesRest = diffInMillies;

    for ( TimeUnit unit : units ) {

        //calculate difference in millisecond 
        long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
        long diffInMilliesForUnit = unit.toMillis(diff);
        milliesRest = milliesRest - diffInMilliesForUnit;

        //put the result in the map
        result.put(unit,diff);
    }

    return result;
}

http://ideone.com/5dXeu6

Вывод похож на Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}, с заказанными единицами измерения.

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


Внимание

Приведенные выше фрагменты кода вычисляют простую разницу между двумя моментами. Это может вызвать проблемы при переходе на летнее время, как описано в этом посте . Это означает, что если вы вычисляете разницу между датами без времени, у вас может отсутствовать день / час.

По моему мнению, разница в датах довольно субъективна, особенно в дни. Вы можете:

  • подсчет количества прошедших 24 часов: день + 1 - день = 1 день = 24 часа

  • подсчитать количество прошедшего времени с учетом перехода на летнее время: день + 1 - день = 1 = 24 часа (но с использованием полуночи и летнего времени это может быть 0 дней и 23 часа)

  • считать число day switches, что означает день + 1 13:00 - день 11:00 = 1 день, даже если прошедшее время составляет всего 2 часа (или 1 час, если есть переход на летнее время: p)

Мой ответ действителен, если ваше определение различий в днях совпадает с первым случаем

с JodaTime

Если вы используете JodaTime, вы можете получить разность для 2-х дат (в миллисекундах с поддержкой ReadableInstant) с:

Interval interval = new Interval(oldInstant, new Instant());

Но вы также можете получить разницу для локальных дат / времени:

// returns 4 because of the leap year of 366 days
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5), PeriodType.years()).getYears() 

// this time it returns 5
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5+1), PeriodType.years()).getYears() 

// And you can also use these static methods
Years.yearsBetween(LocalDate.now(), LocalDate.now().plusDays(365*5)).getYears()
182 голосов
/ 12 октября 2009

API JDK Date, к сожалению, ужасно сломан. Я рекомендую использовать библиотеку Joda Time .

У Joda Time есть понятие времени Интервал :

Interval interval = new Interval(oldTime, new Instant());

РЕДАКТИРОВАТЬ: Между прочим, у Joda есть две концепции: Interval для представления интервала времени между двумя моментами времени (представляют время между 8:00 и 10:00), и Duration, который представляет отрезок времени без фактического Границы времени (например, представляют два часа!)

Если вам нужны только сравнения времени, в большинстве реализаций Date (включая JDK) реализован интерфейс Comparable, который позволяет использовать Comparable.compareTo()

149 голосов
/ 16 августа 2010
int diffInDays = (int)( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) )

Обратите внимание, что это работает с датами UTC, поэтому разница может быть выходной, если вы посмотрите на местные даты. А для правильной работы с местными датами требуется совершенно иной подход из-за перехода на летнее время.

53 голосов
/ 16 августа 2010

Вы должны определить свою проблему более четко. Вы могли бы просто взять количество миллисекунд между двумя Date объектами и поделить на количество миллисекунд в 24 часах, например ... но:

  • Это не будет учитывать часовые пояса - Date всегда в UTC
  • При этом не учитывается переход на летнее время (где могут быть дни, например, продолжительностью всего 23 часа)
  • Даже в UTC, сколько дней осталось с 16 августа по 11 вечера до 18 августа по 2 часа ночи? Это всего 27 часов, значит ли это, что один день? Или это должно быть три дня, потому что он охватывает три даты?
48 голосов
/ 20 апреля 2014

Использование java.time фреймворка, встроенного в Java 8 +:

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println("ISO-8601: " + duration);
System.out.println("Minutes: " + duration.toMinutes());

Выход:

ISO-8601: PT24H10M

Минуты: 1450

Для получения дополнительной информации см. Oracle Tutorial и ISO 8601 стандарт.

39 голосов
/ 23 марта 2014

ТЛ; др

Преобразуйте устаревшие java.util.Date объекты в их замену, java.time.Instant. Затем вычислите прошедшее время как Duration.

Duration d = 
    Duration.between(                   // Calculate the span of time between two moments as a number of hours, minutes, and seconds.
        myJavaUtilDate.toInstant() ,    // Convert legacy class to modern class by calling new method added to the old class.
        Instant.now()                   // Capture the current moment in UTC. About two and a half hours later in this example.
    )
;

d.toString (): PT2H34M56S

d.toMinutes (): 154

d.toMinutesPart (): 34

ISO 8601 Формат: PnYnMnDTnHnMnS

Разумный стандарт ISO 8601 определяет краткое текстовое представление промежутка времени как числа лет, месяцев, дней, часов и т. Д. Стандарт называет такой промежуток продолжительностью . Формат PnYnMnDTnHnMnS, где P означает «Период», T отделяет часть даты от части времени, а между ними находятся цифры, за которыми следует буква.

Примеры:

  • P3Y6M4DT12H30M5S
    три года, шесть месяцев, четыре дня, двенадцать часов, тридцать минут и пять секунд
  • PT4H30M
    Четыре с половиной часа

java.time

Инфраструктура java.time , встроенная в Java 8 и более поздние версии, заменяет проблемные старые классы java.util.Date / java.util.Calendar. Новые классы вдохновлены очень успешным фреймворком Joda-Time , задуманным как его преемник, похожим по концепции, но с новой архитектурой. Определяется JSR 310 . Расширена проектом ThreeTen-Extra . См. Учебное пособие .

Момент

Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичного знака) доля).

Instant instant = Instant.now() ;  // Capture current moment in UTC.

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

Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy `java.util.Date` class to modern `java.time.Instant` class.

промежуток времени

Классы java.time разделили эту идею представления промежутка времени в виде количества лет, месяцев, дней, часов, минут, секунд на две половины:

  • Period для лет, месяцев, дней
  • Duration для дней, часов, минут, секунд

Вот пример.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime future = now.plusMinutes ( 63 );
Duration duration = Duration.between ( now , future );

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

И Period, и Duration используют стандарт ISO 8601 для генерации строкового представления их значения.

System.out.println ( "now: " + now + " to future: " + now + " = " + duration );

сейчас: 2015-11-26T00: 46: 48.016-05: 00 [Америка / Монреаль] до будущего: 2015-11-26T00: 46: 48.016-05: 00 [Америка / Монреаль] = PT1H3M

Java 9 добавляет методы к Duration для получения дней, часов, минут и секунд.

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

long totalHours = duration.toHours();

В Java 9 класс Duration получает новые методы для возврата различных частей дней, часов, минут, секунд, миллисекунд / наносекунд. Вызовите методы to…Part: toDaysPart(), toHoursPart() и т. Д.

ChronoUnit

Если вы заботитесь только о более простой детализации времени, такой как «количество прошедших дней», используйте перечисление ChronoUnit.

long daysElapsed = ChronoUnit.DAYS.between( earlier , later );

Другой пример.

Instant now = Instant.now();
Instant later = now.plus( Duration.ofHours( 2 ) );
…
long minutesElapsed = ChronoUnit.MINUTES.between( now , later );

120


О java.time

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

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

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Где получить классы java.time?

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

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


Joda-Time

ОБНОВЛЕНИЕ: проект Joda-Time теперь находится в режиме обслуживания , и команда рекомендует перейти на классы java.time . Я оставляю этот раздел нетронутым для истории.

Библиотека Joda-Time использует ISO 8601 по умолчанию. Его класс Period анализирует и генерирует эти строки PnYnMnDTnHnMnS.

DateTime now = DateTime.now(); // Caveat: Ignoring the important issue of time zones.
Period period = new Period( now, now.plusHours( 4 ).plusMinutes( 30));
System.out.println( "period: " + period );

Оказывает:

period: PT4H30M
34 голосов
/ 28 декабря 2010
Days d = Days.daysBetween(startDate, endDate);
int days = d.getDays();

https://www.joda.org/joda-time/faq.html#datediff

24 голосов
/ 12 октября 2009

Немного более простая альтернатива:

System.currentTimeMillis() - oldDate.getTime()

Что касается "приятнее": ну что именно вам нужно? Проблема представления временных интервалов в виде количества часов, дней и т. Д. Заключается в том, что это может привести к неточностям и неправильным ожиданиям из-за сложности дат (например, дни могут иметь 23 или 25 часов из-за перехода на летнее время).

22 голосов
/ 21 ноября 2011

Использование миллисекундного подхода может вызвать проблемы в некоторых локалях.

Возьмем, к примеру, разницу между двумя датами 24.03.2007 и 25.03.2007 должна быть 1 день;

Однако, используя миллисекундный маршрут, вы получите 0 дней, если вы запустите его в Великобритании!

/** Manual Method - YIELDS INCORRECT RESULTS - DO NOT USE**/  
/* This method is used to find the no of days between the given dates */  
public long calculateDays(Date dateEarly, Date dateLater) {  
   return (dateLater.getTime() - dateEarly.getTime()) / (24 * 60 * 60 * 1000);  
} 

Лучший способ реализовать это - использовать java.util.Calendar

/** 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;  
}  
21 голосов
/ 03 апреля 2012

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

      Calendar calendar1 = Calendar.getInstance();
      Calendar calendar2 = Calendar.getInstance();
      calendar1.set(2012, 04, 02);
      calendar2.set(2012, 04, 04);
      long milsecs1= calendar1.getTimeInMillis();
      long milsecs2 = calendar2.getTimeInMillis();
      long diff = milsecs2 - milsecs1;
      long dsecs = diff / 1000;
      long dminutes = diff / (60 * 1000);
      long dhours = diff / (60 * 60 * 1000);
      long ddays = diff / (24 * 60 * 60 * 1000);

      System.out.println("Your Day Difference="+ddays);

Выражение print является лишь примером - вы можете отформатировать его так, как вам нравится.

...