Вычисление разницы между двумя экземплярами даты Java

433

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

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

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

pr1001
источник
10
Почему нет любви ко времени йода? Это в значительной степени лучший вариант, если вы собираетесь иметь дело с датами в Java.
Доктор Джонс
7
Пожалуйста, проверьте мое элегантное решение с 2 вкладышами, без использования Joda и выдачи результата в любом TimeUnit по адресу stackoverflow.com/a/10650881/82609
Себастьян Лорбер,
2
Позор всем тем, кто рекомендует время Joda, и не рекомендует истинный ответ Java ...
Zizouz212
2
@ Zizouz212 Что касается рекомендации Joda-Time, старые классы даты и времени в комплекте с Java являются плохими, очень плохими, плохо спроектированными, запутанными и хлопотными. Настолько плохо, что Joda-Time стал чрезвычайно успешным в качестве их замены. Настолько плохо, что даже Sun / Oracle отказались от них и приняли пакет java.time как часть Java 8 и более поздних версий. Классы java.time вдохновлены Joda-Time. Joda-Time и java.time возглавляет один и тот же человек, Стивен Колборн . Я бы сказал, «Позор , кто рекомендует использовать Date, Calendarи SimpleDateFormat».
Василий Бурк
2
Хотя Joda Time, вероятно, был хорошим ответом на вопрос, на сегодняшний день лучшим ответом для любого, кто может использовать Java 8, является использование java.time.Periodи / или Duration. Смотрите ответ Бэзила Бурка ниже .
Оле В.В.

Ответы:

198

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

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

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

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

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

notnoop
источник
кстати - ты имеешь в виду Comparable.compareTo(), а неComparable.compare() .
Скотт Моррисон
У Joda-Time есть три класса, чтобы изобразить промежуток времени различными способами: Интервал, Продолжительность и Период. Этот правильный ответ обсуждает эти первые два. Смотрите мой ответ для информации о периоде.
Василий Бурк
В Java 8 появилась новая дата и время, как в joda.
14:30
11
Новый java.timeпакет в Java 8 вдохновлен Joda-Time, но не является заменой. У каждого есть свои плюсы и минусы. К счастью, вам не нужно выбирать между ними. Используйте каждый для его сильных сторон, пока вы осторожны со своими importзаявлениями. Посмотрите этот другой ответ на примере java.time.
Василий Бурк
6
К вашему сведению, проект Joda-Time сейчас находится в режиме обслуживания , и команда советует перейти на классы java.time . Смотрите Учебник по Oracle .
Василий Бурк
550

Простой 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 перечисление происходит от Nanos до нескольких дней.


Удобный для восприятия 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}, с заказанными единицами.

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


Предупреждение

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

На мой взгляд, разница в датах является субъективной, особенно в дни. Ты можешь:

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

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

  • посчитать число day switches, что означает день + 1 1 вечера - день 11 утра = 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()
Себастьян Лорбер
источник
Ух ты, ты действительно можешь научить старую собаку новым трюкам! Никогда не слышал об этом раньше, очень полезно для перевода различий дат в значимые строки.
Джереми Гуделл
@SebastienLorber В TimeUnit есть способ рассчитать разницу таким образом? «Будильник установлен на 3 дня, 4 часа и 12 минут».
Likejudo
блестящие вещи действительно сэкономили мне много времени. Ценю твою помощь.
Люью я Эдвинсон
Я потратил впустую 3 дня своей жизни, имея дело с датами Java, и я просто отбросил многое из этого и заменил этим. Спасибо.
user1091524
Это лучший ответ, который я видел с тех пор, как пришел на stackoverflow! : D: D: D
Холодно
153
int diffInDays = (int)( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) )

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

Майкл Боргвардт
источник
6
Это на самом деле не работает правильно в Android. Ошибки округления существуют. Пример 19-21 мая говорит 1 день, потому что он разыгрывает 1,99 в 1. Используйте раунд перед приведением к int.
Пратик Мандрекар
4
Это самый лучший и самый простой ответ. При расчете разницы между двумя датами местные часовые пояса вычитают друг друга ... так что правильный ответ (как двойной) дается просто как ((double) (newer.getTime () - old.getTime ()) / (86400.0 * 1000.0); ... как двойной, у вас также есть дробный день, который можно легко преобразовать в ЧЧ: ММ: сс.
scottb
8
@scottb: проблема с локальными датами в том, что у вас может быть летнее время, что означает, что в некоторых днях по 23 или 25 часов, что может испортить результат.
Майкл Боргвардт
1
@ Steve: это 28 для меня, когда я определяю их как эту новую дату (115, 2, 26); (обратите внимание на документ API для параметров). Возможно ли, что вы анализируете их, используя формат даты США и в снисходительном режиме, чтобы 26.03.2015 интерпретировался как 3-й день 26-го месяца 2015 года?
Майкл Боргвардт
1
@Steve: хм, не уверен, почему раньше я получил другой результат, но теперь я могу воспроизвести вашу ошибку. Брекеты в вашем коде отличаются от моего ответа, который приводит (endDate.getTime() - startDate.getTime())к приведению intвместо конечного результата, и вы получаете целочисленное переполнение.
Майкл Боргвардт
54

Используя инфраструктуру 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 и стандарт ISO 8601 .

Виталий Федоренко
источник
5
Это или использовать Periodвместо Durationдаты.
Aphex
53

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

  • Это не будет учитывать часовые пояса - Dateвсегда в UTC
  • Это не учитывает переход на летнее время (например, дни могут длиться всего 23 часа)
  • Сколько дней, даже в UTC, с 16 августа по 11 вечера до 18 августа по 2 часа ночи? Это только 27 часов, значит ли это, что один день? Или это должно быть три дня, потому что он охватывает три даты?
Джон Скит
источник
1
Я думал java.util. Дата была просто крошечной оберткой вокруг представления в миллисекундах времени, интерпретируемой в местном (по умолчанию) часовом поясе. Распечатка даты дает мне местное представление часового пояса. Я здесь запутался?
Адриан Костер
46

ТЛ; др

Преобразуйте ваши устаревшие 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, и более .


Joda времени

ОБНОВЛЕНИЕ: проект 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
Базилик Бурк
источник
Забавно, что вы начали не относящуюся к вопросу стену текста с "tl; dr". Вопрос не в том, как получить временную метку от X назад или в единицах времени X, а в том, как получить дельту между двумя датами. Вот и все.
Буффало
@Buffalo Я не понимаю твой комментарий. Ответ включает в себя Period, Durationи ChronoUnit, три класса , цель которых состоит в определении дельты между точками времени. Пожалуйста, укажите, какие части моей «стены текста» не имеют значения. И вы не правы, говоря, что Вопрос задал «дельту между датами»: Вопрос задал дельту между Dateобъектами, которые являются моментами даты и времени, а не «датами», несмотря на неудачное именование этого класса.
Василий Бурк
В вашем комментарии нет ничего, что обрабатывало бы два существующих объекта java.util.Date. Я пытался использовать различные фрагменты кода и не нашел ничего, что использовало бы объект java.util.Date.
Буффало
@ Buffalo Справедливо, хорошая критика. Я добавил код в раздел "tl; dr", конвертирующий из a java.util.Dateв Instant(просто вызов .toInstant). И я добавил Momentраздел, чтобы объяснить. Вы упомянули пару Dateобъектов, но на самом деле Вопрос использует только один существующий Dateобъект, а затем фиксирует текущий момент, поэтому я сделал то же самое. Спасибо за ответ! Совет: откажитесь от использования Date- эти классы действительно ужасно ужасны.
Василий Бурк
23

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

System.currentTimeMillis() - oldDate.getTime()

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

Майкл Боргвардт
источник
22

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

Возьмем, к примеру, разницу между двумя датами 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;  
}  
Засадный
источник
19
Не могли бы вы указать первоначального автора этого кода и третье предложение, чья запись в блоге датируется 2007 годом ?
wchargin
Разве это не немного неэффективно?
Гангнус
Это не работает правильно. daysBetween (новый GregorianCalendar (2014,03,01), новый GregorianCalendar (2014,04,02))); возвращает 31, и он должен вернуть 32: timeanddate.com/date/...
marcolopes
5
@marcolopes - Вы не правы - потому что календарные месяцы начинаются с нуля. Я уверен, что вы имеете в виду март / апрель, но вы тестируете апрель / июнь, который равен 31. На всякий случай напишите это как -> new GregorianCalendar (2014, Calendar.MARCH, 1) ....
Дядя Иро
@UncleIroh, ты прав! Я пропустил этот важный факт (календарные месяцы начинаются с нуля). Я должен пересмотреть этот вопрос снова и снова.
marcolopes
22

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

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

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

JDGuide
источник
5
@ Manoj Kumar Bardhan: добро пожаловать в стек переполнения: Как вы видите, вопрос и ответы лет. Ваш ответ должен добавить больше к вопросу / ответу, чем существующие.
Jayan
3
А как насчет дней, которые имеют 23 или 25 часов из-за перехода на летнее время?
Джони
19

Поскольку все ответы здесь верны, но используют устаревшие java или сторонние библиотеки, такие как joda или аналогичные, я просто оставлю другой путь, используя новые классы java.time в Java 8 и более поздних версиях. Смотрите Oracle Tutorial .

Используйте LocalDateи ChronoUnit:

LocalDate d1 = LocalDate.of(2017, 5, 1);
LocalDate d2 = LocalDate.of(2017, 5, 18);

long days = ChronoUnit.DAYS.between(d1, d2);
System.out.println( days );
ΦXocę 웃 Пепеупа ツ
источник
9

Вычитание дат в миллисекундах работает (как описано в другом посте), но вы должны использовать HOUR_OF_DAY, а не HOUR при очистке временных частей ваших дат:

public static final long MSPERDAY = 60 * 60 * 24 * 1000;
...
final Calendar dateStartCal = Calendar.getInstance();
dateStartCal.setTime(dateStart);
dateStartCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateStartCal.set(Calendar.MINUTE, 0);
dateStartCal.set(Calendar.SECOND, 0);
dateStartCal.set(Calendar.MILLISECOND, 0);
final Calendar dateEndCal = Calendar.getInstance();
dateEndCal.setTime(dateEnd);
dateEndCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateEndCal.set(Calendar.MINUTE, 0);
dateEndCal.set(Calendar.SECOND, 0);
dateEndCal.set(Calendar.MILLISECOND, 0);
final long dateDifferenceInDays = ( dateStartCal.getTimeInMillis()
                                  - dateEndCal.getTimeInMillis()
                                  ) / MSPERDAY;
if (dateDifferenceInDays > 15) {
    // Do something if difference > 15 days
}
Малкольм Бекхофф
источник
1
Это лучший способ определить разницу между двумя календарными датами, не выходя за пределы стандартных библиотек. Большинство других ответов рассматривают день как произвольный 24-часовой период. Если бы високосные секунды когда-либо вычитали, а не добавляли, окончательный расчет мог быть отключен на единицу из-за усечения, поскольку абсолютная разница могла быть немного меньше, чем целое число, кратное MSPERDAY.
JulianSymes
8

Если вы не хотите использовать JodaTime или аналогичный, лучшее решение, вероятно, это:

final static long MILLIS_PER_DAY = 24 * 3600 * 1000;
long msDiff= date1.getTime() - date2.getTime();
long daysDiff = Math.round(msDiff / ((double)MILLIS_PER_DAY));

Количество мс в день не всегда одинаково (из-за летнего времени и високосных секунд), но оно очень близко, и, по крайней мере, отклонения из-за перехода на летнее время отменяются в течение более длительных периодов. Поэтому деление, а затем округление дадут правильный результат (по крайней мере до тех пор, пока используемый локальный календарь не содержит странных скачков времени, кроме DST и високосных секунд).

Обратите внимание, что это все еще предполагает, что date1и date2установлено в то же время суток. Для разного времени дня сначала нужно определить, что означает «разница в дате», как указал Джон Скит.

sleske
источник
Вопрос в date1.getTime()против date2.getTime(). Таким образом, разность с 2016-03-03 по 2016-03-31 будет составлять 27 дней, а вам потребуется 28 дней.
Малат
@malat: Извините, я не могу следовать. Я только что попробовал - разность 2016-03-03 до 2016-03-31 с моим кодом вычисляет 28 дней. Если он вычисляет что-то еще в вашем примере, подумайте над тем, чтобы задать это как отдельный вопрос.
Слеск
Кроме того, вы читали мою оговорку? «это все еще предполагает, что date1 и date2 установлены на одно и то же время дня». Возможно, вы использовали разное время дня в своем тесте?
Слеське
Ах да! Я пропустил трюк Math.round(), который кажется безопасным в календарях «реального мира». Извините за шум.
Малат
8

Взгляните на Joda Time , который представляет собой улучшенный API Date / Time для Java и должен отлично работать со Scala.

Роб Х
источник
заменено на java.time (JSR-310) в Java 8
малат
5

Позвольте мне показать разницу между Joda Interval и Days:

DateTime start = new DateTime(2012, 2, 6, 10, 44, 51, 0);
DateTime end = new DateTime(2012, 2, 6, 11, 39, 47, 1);
Interval interval = new Interval(start, end);
Period period = interval.toPeriod();
System.out.println(period.getYears() + " years, " + period.getMonths() + " months, " + period.getWeeks() + " weeks, " + period.getDays() + " days");
System.out.println(period.getHours() + " hours, " + period.getMinutes() + " minutes, " + period.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *1 weeks, 1 days*
//0 hours, 54 minutes, 56 seconds 

//Period can set PeriodType,such as PeriodType.yearMonthDay(),PeriodType.yearDayTime()...
Period p = new Period(start, end, PeriodType.yearMonthDayTime());
System.out.println(p.getYears() + " years, " + p.getMonths() + " months, " + p.getWeeks() + " weeks, " + p.getDays() + "days");
System.out.println(p.getHours() + " hours, " + p.getMinutes() + " minutes, " + p.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *0 weeks, 8 days*
//0 hours, 54 minutes, 56 seconds 
user674158
источник
5

Если вам нужна отформатированная возвращаемая строка типа «2 дня 03h 42m 07s», попробуйте это:

public String fill2(int value)
{
    String ret = String.valueOf(value);

    if (ret.length() < 2)
        ret = "0" + ret;            
    return ret;
}

public String get_duration(Date date1, Date date2)
{                   
    TimeUnit timeUnit = TimeUnit.SECONDS;

    long diffInMilli = date2.getTime() - date1.getTime();
    long s = timeUnit.convert(diffInMilli, TimeUnit.MILLISECONDS);

    long days = s / (24 * 60 * 60);
    long rest = s - (days * 24 * 60 * 60);
    long hrs = rest / (60 * 60);
    long rest1 = rest - (hrs * 60 * 60);
    long min = rest1 / 60;      
    long sec = s % 60;

    String dates = "";
    if (days > 0) dates = days + " Days ";

    dates += fill2((int) hrs) + "h ";
    dates += fill2((int) min) + "m ";
    dates += fill2((int) sec) + "s ";

    return dates;
}
Инго
источник
2
Вау, зачем бросать свои, когда Joda-Time предоставляет класс Period, уже написанный и отлаженный?
Василий Бурк
8
Зачем мне скачивать библиотеку со сжатым размером файла 4,1 МБ и добавлять ее в мой проект, когда мне нужно всего 32 строки кода ???
Инго
1
Потому что Joda-Time похож на картофельные чипсы: нельзя есть только одну. Вы будете использовать Joda-Time повсюду. И потому, что Joda-Time - это хорошо проверенный и изношенный код. И потому что Joda-Time анализирует и генерирует стандартные строки длительности ISO 8601, которые могут быть полезны для сериализации или сообщения этих значений. И потому, что Joda-Time включает в себя форматеры для печати локализованных представлений этих значений.
Василий Бурк,
Не весь ваш код проверен. stdвыше не определено.
Василий Бурк
@Basil Bourque: спасибо за подсказку. Я установил источник. Эта ошибка возникла при переводе на английский.
Инго
5

Примечание: startDate и endDates -> java.util.Date

import org.joda.time.Duration;
import org.joda.time.Interval;
// Use .getTime() unless it is a joda DateTime object
Interval interval = new Interval(startDate.getTime(), endDate.getTime());
Duration period = interval.toDuration();
//gives the number of days elapsed between start 
period.getStandardDays();

и дата окончания

Как и в дни, вы также можете получить часы, минуты и секунды

period.getStandardHours();
period.getStandardMinutes();
period.getStandardSeconds();
Шраван Рамамурти
источник
4

Посмотрите пример здесь http://www.roseindia.net/java/beginners/DateDifferent.shtml Этот пример показывает разницу в днях, часах, минутах, секундах и миллисекундах :).

import java.util.Calendar;
import java.util.Date;

public class DateDifferent {
    public static void main(String[] args) {
        Date date1 = new Date(2009, 01, 10);
        Date date2 = new Date(2009, 07, 01);
        Calendar calendar1 = Calendar.getInstance();
        Calendar calendar2 = Calendar.getInstance();
        calendar1.setTime(date1);
        calendar2.setTime(date2);
        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
-1 Это неправильно, если экземпляры Date не были получены из времени UTC. Смотрите ответ Джона Скита.
слеське
4

Используйте часовой пояс GMT, чтобы получить экземпляр Календаря, установите время, используя метод set класса Calendar. Часовой пояс GMT имеет смещение 0 (не очень важно) и флаг летнего времени, установленный на false.

    final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 9);
    cal.set(Calendar.DAY_OF_MONTH, 29);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date startDate = cal.getTime();

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 12);
    cal.set(Calendar.DAY_OF_MONTH, 21);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date endDate = cal.getTime();

    System.out.println((endDate.getTime() - startDate.getTime()) % (1000l * 60l * 60l * 24l));
Michal
источник
Не забудьте обнулить миллисекундную часть, иначе это хороший ответ в контексте старых классов java.util.Dateи т. Д. Использование хака для установки часового пояса в GMT делает код нечувствительным к эффектам перехода на летнее время.
Мено Хохшильд
4

Следующий код может дать вам желаемый результат:

String startDate = "Jan 01 2015";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
LocalDate date = LocalDate.parse(startDate, formatter);

String currentDate = "Feb 11 2015";
LocalDate date1 = LocalDate.parse(currentDate, formatter);

System.out.println(date1.toEpochDay() - date.toEpochDay());
Вишал Гянджаре
источник
3
Можете ли вы объяснить, что именно делает ваш код? Кажется, что вопрос задается о дельте времени, и, на первый взгляд, ваш код, похоже, возвращает дельту дня?
GHC
4
public static String getDifferenceBtwTime(Date dateTime) {

    long timeDifferenceMilliseconds = new Date().getTime() - dateTime.getTime();
    long diffSeconds = timeDifferenceMilliseconds / 1000;
    long diffMinutes = timeDifferenceMilliseconds / (60 * 1000);
    long diffHours = timeDifferenceMilliseconds / (60 * 60 * 1000);
    long diffDays = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24);
    long diffWeeks = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 7);
    long diffMonths = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 30.41666666));
    long diffYears = (long)(timeDifferenceMilliseconds / (1000 * 60 * 60 * 24 * 365));

    if (diffSeconds < 1) {
        return "one sec ago";
    } else if (diffMinutes < 1) {
        return diffSeconds + " seconds ago";
    } else if (diffHours < 1) {
        return diffMinutes + " minutes ago";
    } else if (diffDays < 1) {
        return diffHours + " hours ago";
    } else if (diffWeeks < 1) {
        return diffDays + " days ago";
    } else if (diffMonths < 1) {
        return diffWeeks + " weeks ago";
    } else if (diffYears < 12) {
        return diffMonths + " months ago";
    } else {
        return diffYears + " years ago";
    }
}   
Парт Соланки
источник
3
int daysDiff = (date1.getTime() - date2.getTime()) / MILLIS_PER_DAY;
Bozho
источник
5
-1 Это неправильно, если экземпляры Date не были получены из времени UTC. Смотрите ответ Джона Скита.
слеське
5
@ Слеске, ты не прав. Он уже потерял информацию о часовом поясе с помощью Date, поэтому это сравнение является лучшим, что может быть достигнуто с учетом обстоятельств.
Божо
1
ОК, думаю, мы оба правы. Это правда, что это зависит от того, откуда приходит экземпляр Date. Тем не менее, это настолько распространенная ошибка, что следует упомянуть.
слеське
Когда я использую ваше решение, оно говорит: Несоответствие типов: невозможно преобразовать из long в int. Я думаю, что вы также должны добавить кастинг или я что-то упустил?
Ad Infinitum
3

Лучше всего сделать

(Date1-Date2)/86 400 000 

Это число - количество миллисекунд в дне.

Одна дата другая дата дает вам разницу в миллисекундах.

Соберите ответ в двойной переменной.

Ревант Кумар
источник
Да, почему бы и нет? Я не понимаю суть вопроса в некотором смысле: OP имеет разницу в мс ... и есть определенное количество мс в день (хорошо с некоторыми астрономическими корректировками один раз в синей луне, но OP не делает не говорите, что оно должно быть настолько точным, насколько это нужно астрономам ...)
Майк Грызун
3

Вот правильное решение Java 7 в O (1) без каких-либо зависимостей.

public static int countDaysBetween(Date date1, Date date2) {

    Calendar c1 = removeTime(from(date1));
    Calendar c2 = removeTime(from(date2));

    if (c1.get(YEAR) == c2.get(YEAR)) {

        return Math.abs(c1.get(DAY_OF_YEAR) - c2.get(DAY_OF_YEAR)) + 1;
    }
    // ensure c1 <= c2
    if (c1.get(YEAR) > c2.get(YEAR)) {
        Calendar c = c1;
        c1 = c2;
        c2 = c;
    }
    int y1 = c1.get(YEAR);
    int y2 = c2.get(YEAR);
    int d1 = c1.get(DAY_OF_YEAR);
    int d2 = c2.get(DAY_OF_YEAR);

    return d2 + ((y2 - y1) * 365) - d1 + countLeapYearsBetween(y1, y2) + 1;
}

private static int countLeapYearsBetween(int y1, int y2) {

    if (y1 < 1 || y2 < 1) {
        throw new IllegalArgumentException("Year must be > 0.");
    }
    // ensure y1 <= y2
    if (y1 > y2) {
        int i = y1;
        y1 = y2;
        y2 = i;
    }

    int diff = 0;

    int firstDivisibleBy4 = y1;
    if (firstDivisibleBy4 % 4 != 0) {
        firstDivisibleBy4 += 4 - (y1 % 4);
    }
    diff = y2 - firstDivisibleBy4 - 1;
    int divisibleBy4 = diff < 0 ? 0 : diff / 4 + 1;

    int firstDivisibleBy100 = y1;
    if (firstDivisibleBy100 % 100 != 0) {
        firstDivisibleBy100 += 100 - (firstDivisibleBy100 % 100);
    }
    diff = y2 - firstDivisibleBy100 - 1;
    int divisibleBy100 = diff < 0 ? 0 : diff / 100 + 1;

    int firstDivisibleBy400 = y1;
    if (firstDivisibleBy400 % 400 != 0) {
        firstDivisibleBy400 += 400 - (y1 % 400);
    }
    diff = y2 - firstDivisibleBy400 - 1;
    int divisibleBy400 = diff < 0 ? 0 : diff / 400 + 1;

    return divisibleBy4 - divisibleBy100 + divisibleBy400;
}


public static Calendar from(Date date) {

    Calendar c = Calendar.getInstance();
    c.setTime(date);

    return c;
}


public static Calendar removeTime(Calendar c) {

    c.set(HOUR_OF_DAY, 0);
    c.set(MINUTE, 0);
    c.set(SECOND, 0);
    c.set(MILLISECOND, 0);

    return c;
}
suxumuxu
источник
2

Это, вероятно, самый простой способ сделать это - возможно, потому, что я уже некоторое время кодирую на Java (с его по общему признанию неуклюжими библиотеками даты и времени), но этот код выглядит «простым и приятным» для меня!

Довольны ли вы результатом, возвращаемым в миллисекундах, или частью вашего вопроса является то, что вы бы предпочли, чтобы он был возвращен в каком-то альтернативном формате?

Анджей Дойл
источник
2

Не используя стандартный API, нет. Вы можете свернуть свой собственный, выполнив что-то вроде этого:

class Duration {
    private final TimeUnit unit;
    private final long length;
    // ...
}

Или вы можете использовать Joda :

DateTime a = ..., b = ...;
Duration d = new Duration(a, b);
gustafc
источник
2

Просто чтобы ответить на первоначальный вопрос:

Поместите следующий код в функцию типа Long getAge () {}

Date dahora = new Date();
long MillisToYearsByDiv = 1000l *60l * 60l * 24l * 365l;
long javaOffsetInMillis = 1990l * MillisToYearsByDiv;
long realNowInMillis = dahora.getTime() + javaOffsetInMillis;
long realBirthDayInMillis = this.getFechaNac().getTime() + javaOffsetInMillis;
long ageInMillis = realNowInMillis - realBirthDayInMillis;

return ageInMillis / MillisToYearsByDiv;

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

:)

MugiWara No Carlos
источник
2

Поскольку вопрос помечен с помощью Scala,

import scala.concurrent.duration._
val diff = (System.currentTimeMillis() - oldDate.getTime).milliseconds
val diffSeconds = diff.toSeconds
val diffMinutes = diff.toMinutes
val diffHours = diff.toHours
val diffDays = diff.toDays
VasiliNovikov
источник
2

После прохождения всех остальных ответов, чтобы сохранить тип даты Java 7, но быть более точным / стандартным с подходом Java 8 diff,

public static long daysBetweenDates(Date d1, Date d2) {
    Instant instant1 = d1.toInstant();
    Instant instant2 = d2.toInstant();
    long diff = ChronoUnit.DAYS.between(instant1, instant2);
    return diff;
}
ген б.
источник
1

попробуй это:

int epoch = (int) (new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse("01/01/1970  00:00:00").getTime() / 1000);

Вы можете редактировать строку в параметре parse () param.

sabbibJAVA
источник