Обчислення різниці між двома екземплярами дати Java


433

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

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

Однак це просто залишає на мене longмільйони секунд. Чи є простіший, приємніший спосіб отримати дельту часу?


10
Чому немає любові до часу joda? Це майже найкращий варіант, якщо ви збираєтеся мати справу з побаченнями в 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 .

Joda Time має концепцію інтервалу часу :

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

EDIT: До речі, Joda має два поняття: Intervalпредставлення інтервалу часу між двома моментами часу (представляють час між 8 ранку та 10 ранку), і a, Durationщо представляє тривалість часу без фактичних часових меж (наприклад, представляють дві години!)

Якщо вас цікавлять лише порівняння часу, більшість Dateреалізацій (включаючи JDK один) реалізує Comparableінтерфейс, який дозволяє використовуватиComparable.compareTo()


btw - ти маєш на увазі Comparable.compareTo(), ні Comparable.compare().
Скотт Моррісон

Joda-Time має три класи, щоб зобразити проміжок часу різними способами: інтервал, тривалість та період. Ця правильна відповідь обговорює це перші два. Дивіться мою відповідь для інформації про Період.
Василь Бурк

У Java 8 з'явилися нові дати та час, такі як joda.
tbodt

11
Новий java.timeпакет Java 8 натхненний Joda-Time, але не є заміною. У кожного є свої плюси і мінуси. На щастя, вам не доведеться вибирати між ними. Використовуйте кожен для його сильних сторін, поки ви обережні зі своїми importтвердженнями. Дивіться цю іншу відповідь, наприклад, java.time.
Василь Бурк

6
FYI, проект Joda-Time зараз перебуває в режимі обслуговування , команда радить перейти до класів java.time . Дивіться Підручник від Oracle .
Василь Бурк

550

Простий діф (без вкладки)

/**
 * 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, який переходить від наносу до днів.


Людина, що читається різниці (без вкладки)

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 1 вечора - день 11 ранку = 1 день, навіть якщо минулий час складає всього 2 години (або 1 год, якщо є літній час: p)

Моя відповідь справедлива, якщо ваше визначення дати відрізняється по днях, що відповідають 1-му випадку

З JodaTime

Якщо ви використовуєте JodaTime, ви можете отримати різницю для двох моментів (підтримуваних мільйонами 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

блискучі речі дійсно заощадили багато мого часу. Вдячний за вашу допомогу.
Lyju I Edwinson

Я витратив 3 дні свого життя, займаючись датами Java, і я просто викинув багато цього і замінив цим. Дякую.
користувач1091524

Це найкраща відповідь, яку я бачив з моменту приходу на 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.
Пратік Мандрекар

4
Це найкраща і найпростіша відповідь. Підраховуючи різницю між двома датами, місцеві часові пояси віднімають один одного ... так що правильну відповідь (як подвійну) задають просто ((подвійний) (newer.getTime () - старший.getTime ()) / (86400.0 * 1000.0); ... як подвійний, у вас є і дробовий день, який можна легко перетворити на HH: MM: ss
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дат.
Афекс

53

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

  • Це не враховує часових поясів - Dateзавжди в UTC
  • Це не враховуватиме літній час (наприклад, дні, тривалістю лише 23 години)
  • Навіть за UTC, скільки днів у 16 ​​серпня до 11 вечора до 18 серпня 2 ранку? Це всього 27 годин, значить, це означає один день? Або це повинно бути три дні, оскільки він охоплює три дати?

1
Я подумав java.util. Дата була лише крихітною обгорткою навколо представленого часом мільйону, інтерпретованого в локальному часовому поясі (за замовчуванням). Друк дати дає місцеве представлення часового поясу. Я тут розгублений?
Адріан Костер

46

тл; д-р

Перетворення застарілих java.util.Dateоб'єктів в їх заміні, java.time.Instant. Потім обчисліть минулий час як a 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 . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - 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-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

Смішно, що ви розпочали нерелевантну для запитання стінку тексту з "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

Хіба це трохи неефективно?
Gangnus

Це працює не правильно. дні між (новий GregorianCalendar (2014,03,01), новий GregorianCalendar (2014,04,02))); повертається 31, і він повинен повернутися 32: timeanddate.com/date/…
marcolopes

5
@marcolopes - Ви помиляєтесь, тому що календарні місяці базуються на нулі. Я впевнений, що ви маєте на увазі марш / квітень, але те, що ви тестуєте, це квітень / червень, який становить 31. Для безпечності напишіть це як -> новий GregorianCalendar (2014, Calendar.MARCH, 1) ....
Дядько Ірох

@UncleIroh, ти маєш рацію! Я пропустив той важливий факт (календарні місяці базуються на нулі). Я мушу ще раз переглянути це питання.
марколопи

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: Ласкаво просимо, щоб переповнювати стек: Як бачите, питання та відповіді років. Ваша відповідь повинна додати більше запитань / відповідей, ніж існуючих.
Джаян

3
А як щодо днів, які мають 23 або 25 годин через перехід DST?
Джоні

19

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

Використання 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 днів. Якщо він обчислює щось інше у вашому прикладі, розгляньте це як окреме запитання.
sleske

Також ти читав мій застереження? "все ще передбачає, що дата1 та дата2 встановлені в один і той же час доби". Ви, можливо, використовували різний час дня у своєму тесті?
sleske

Ага так! Я пропустив трюк із Math.round(), що здається безпечним у календарях "реального світу". Вибачте за шум.
malat

8

Погляньте на Joda Time , який є вдосконаленим API Date / Time для Java і повинен добре працювати зі Scala.


замінено java.time (JSR-310) на Java 8
malat

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
Whoa, навіщо катати своє, коли Joda-Time надає клас Period, вже написаний та налагоджений?
Василь Бурк

8
Навіщо мені завантажувати бібліотеку зі стиснутим розміром файлів у 4,1 Мб та додати її до свого проекту, коли мені потрібно лише 32 рядки коду ???
Інго

1
Тому що Joda-Time схожий на картопляні чіпси: не можна їсти лише одну. Ви будете використовувати Joda-Time всюди. А тому, що Joda-Time добре перевірений і добре зношений код. А тому, що Joda-Time розбирає та генерує стандартні рядки тривалості ISO 8601, які можуть бути зручними для серіалізації та повідомлення цих значень. А тому, що Joda-Time включає форматери для друку локалізованих уявлень цих значень.
Василь Бурк

Не весь ваш код перевірений. stdвище не визначено.
Василь Бурк

@Basil Bourque: Thx для підказки. Я визначив джерело. Ця помилка сталася при перекладі на англійську мову.
Інго

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 Це неправильно, якщо випадки дати не були отримані за часом UTC. Дивіться відповідь Джона Скіта.
sleske

4

Використовуйте часовий пояс GMT, щоб отримати екземпляр Календаря, встановіть час, використовуючи встановлений метод класу Календар. Часовий пояс 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 Це неправильно, якщо випадки дати не були отримані за часом UTC. Дивіться відповідь Джона Скіта.
sleske

5
@sleske ти не правий. Він уже втратив інформацію про часовий пояс, використовуючи Date, тож це порівняння є найкращим, що можна досягти, враховуючи обставини.
Божо

1
Гаразд, здогадайтесь, ми обидва праві. Це правда, що це залежить від того, звідки походить екземпляр Date. І все-таки це така поширена помилка, що її слід згадати.
sleske

Коли я використовую ваше рішення, він говорить: Введіть невідповідність: не можна перетворити з long в Int. Я думаю, ви також повинні додати кастинг чи я щось пропускаю?
Ad Infinitum

3

Найкраще це зробити

(Date1-Date2)/86 400 000 

Це число - це кількість мілісекунд за день.

Одна дата-інша дата дає вам різницю в мілісекундах.

Зберіть відповідь у подвійній змінній.


Так, чому ні? Я не розумію суть питання в чомусь: ОП має різницю в мс ... і в день є певна кількість мс (гаразд з певними астрономічними налаштуваннями один раз на синьому місяці, але ОП не відповідає не скажу, що він повинен бути настільки точним, як це потребують астрономи ...)
гризун Майка

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,

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.