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


433

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

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

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


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


кстати - ты имеешь в виду 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);

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


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

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установлено в то же время суток. Для разного времени дня сначала нужно определить, что означает «разница в дате», как указал Джон Скит.


Вопрос в 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 

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

Не забудьте обнулить миллисекундную часть, иначе это хороший ответ в контексте старых классов 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;

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

2

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

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


2

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

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

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

DateTime a = ..., b = ...;
Duration d = new Duration(a, b);

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 применяет в исчислении дат.

:)


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

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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.