Сумма двух дат в Java

Как добавить две даты в Java?

Пример: сумма "2010-01-14 19:16:17" "0000-10-03 01:10:05"
приведет к "2010-11-17 20:26:22".

Я знаю, как это сделать, используя Calendar и добавляя поле по полю.

Есть ли другой способ суммировать их все (год/месяц/день/час/минута/секунда) сразу?

Ответы

Ответ 1

Если вы используете объект Date, вы можете просто сделать:

Date d1 = ...
Date d2 = ...

long sum = d1.getTime() + d2.getTime();

Date sumDate = new Date(sum);

В коде используется метод .getTime(), который возвращает число миллисекунд с эпохи. Излишне говорить, что класс Date имеет много проблем и его следует избегать, когда это возможно.

Вместо этого вы хотите суммировать другие типы?

Обновление: для Calendar, я бы сделал следующее (на основе javadocs):

Calendar c1 = ...
Calendar c2 = ...
long sum = c1.getTimeInMillis() + c2.getTimeInMillis();
Calendar sumCalendar = (Calendar)c1.clone();
sumCalendar.setTimeInMillis(sum);

ОБНОВЛЕНО: Как сказал Стив, это работает, если в Дате, которую вы представили здесь, предполагается, что вторая дата относится к эпохе Java. Если вы хотите начать с года "0", вам нужно учесть это (путем вычитания времени вашей эпохи).

Ответ 2

Как всегда, я бы порекомендовал API даты/времени Java 8 или Joda для работы с датой/временем, поскольку они гораздо более мощные и интуитивно понятные.

Вы можете добавлять длительности и периоды в объект DateTime тривиально. Вы можете добавить минуты/секунды/месяцы одинаково легко.

Однако вы не можете добавить две даты напрямую, так как это не имеет смысла. Это убедительная иллюстрация того, почему Joda помогает - она мешает вам делать то, что вам действительно не следует делать.

Ответ 3

Не суммируйте время в миллисерах двух дат!

Date d1 = new Date();
Date d2 = new Date();
Date dTotal = new Date(d1.getTime() + d2.getTime());
System.out.println(dTotal); // Incorrect! Misses about 1970 years.

Просто клонируйте Calendar и добавьте части datetime по одному.

Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
Calendar cTotal = (Calendar) c1.clone();
cTotal.add(Calendar.YEAR, c2.get(Calendar.YEAR));
cTotal.add(Calendar.MONTH, c2.get(Calendar.MONTH) + 1); // Months are zero-based!
cTotal.add(Calendar.DATE, c2.get(Calendar.DATE));
cTotal.add(Calendar.HOUR_OF_DAY, c2.get(Calendar.HOUR_OF_DAY));
cTotal.add(Calendar.MINUTE, c2.get(Calendar.MINUTE));
cTotal.add(Calendar.SECOND, c2.get(Calendar.SECOND));
cTotal.add(Calendar.MILLISECOND, c2.get(Calendar.MILLISECOND));
System.out.println(cTotal.getTime()); // Correct!

Излишне говорить, что JodaTime умнее и чище с этим.

Ответ 4

ТЛ; др

LocalDateTime later = 
    LocalDateTime
    .parse ( 
        "2010-01-14 19:16:17"
        .replace ( " " , "T" ) 
    )
    .plus( Period.parse ( "P10M3D" ) )
    .plus( Duration.parse ( "PT1H10M5S" ) ) 
;

ISO 8601

Представление промежутка времени в том же формате, что и момент, создает путаницу. Пролет совсем не то же самое, что момент.

Вместо использования формата YYYY-MM-DD HH-MM-SS течение определенного промежутка времени я предлагаю использовать стандартный формат ISO 8601 PnYnMnDTnHnMnS. В этом формате P обозначает начало (предположительно, "Период"), а T отделяет часть года-месяца-дня от части часов-минут-секунд.

Пример значений:

  • PT1H30M → Полтора часа.
  • P3Y6M4DT12H30M5S → Три года, шесть месяцев, четыре дня, двенадцать часов, тридцать минут и пять секунд.
  • P10M3DT1H10M5S → Ваши вопросы продолжительностью 0000-10-03 01:10:05.

java.time

В Вопросе и других Ответах используются проблемные старые классы даты и времени, которые теперь устарели из фреймворка java.time, встроенного в Java 8 и более поздние версии. Смотрите Oracle Tutorial. Большая часть функциональности java.time была перенесена на Java 6 и 7 в ThreeTen-Backport и дополнительно адаптирована для Android в ThreeTenABP.

Классы java.time по умолчанию используют форматы ISO 8601 при разборе и генерации строк, представляющих значения даты и времени.

Вопрос не предоставляет никакой информации о часовом поясе, поэтому здесь мы используем класс LocalDateTime. Если мы знаем смещение от UTC, мы будем использовать класс OffsetDateTime, а если еще лучше, мы будем знать часовой пояс, мы будем использовать класс ZonedDateTime.

Промежутки времени в java.time делятся на пару классов. Годы-месяцы-дни представлены классом Period, а часы-минуты-секунды обрабатываются классом Duration.

Объединяя эти времена, мы действительно можем выполнять математику даты и времени. Здесь мы добавляем промежуток времени к начальной дате и времени, чтобы получить итоговую дату и время. И мы делаем это в очень мало строк кода. Результат действительно ожидаемый Вопросом.

Мы преобразуем входные строки в канонический формат ISO 8601, заменив пробел в середине буквой T

LocalDateTime ldt = LocalDateTime.parse ( "2010-01-14 19:16:17".replace ( " " , "T" ) );
//"0000-10-03 01:10:05"
Period period = Period.parse ( "P10M3D" );
Duration duration = Duration.parse ( "PT1H10M5S" );
LocalDateTime result = ldt.plus ( period ).plus ( duration );

Сравните с результатом, ожидаемым в Вопросе.

LocalDateTime expectation = LocalDateTime.parse ( "2010-11-17 20:26:22".replace ( " " , "T" ) );
Boolean isSame = result.equals ( expectation );

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

System.out.println ( "ldt: " + ldt + " + period: " + period + " + duration: " + duration + " is result: " + result + " compared to expectation: " + expectation + " is the same: " + isSame );

ldt: 2010-01-14T19: 16: 17 + период: P10M3D + продолжительность: PT1H10M5S - результат: 2010-11-17T20: 26: 22 по сравнению с ожиданием: 2010-11-17T20: 26: 22 - то же самое: верно

Ответ 5

Вы хотите сделать getTimeInMillis() на обоих этих Календари, чтобы у вас было два значения честности и доброты long, которые вы можете добавить. Затем вы можете взять сумму и занести ее в новый календарь, используя этот метод Calendar setTimeInMillis().

Если вы хотите добавить два Calendar, как показано выше, или два Date, как показано в nonoop, ответ зависит от вас, конечно. Эффект подобен, он просто зависит от того, что вы хотите сделать с результатом. A Date в основном просто хорош для хранения и/или преобразования в String для распечатки или отображения, тогда как Calendar позволит вам играть с отдельными значениями времени, если вы этого захотите.

Как уже упоминалось, вы используете концептуальное no-no при использовании Date или Calendar, которые предназначены для хранения "реальных" дат и времени, например. в 20 или 21 веке, как интервалы, то есть промежутки времени. Классы в стандартной библиотеке Java не дают вам действительно полезных инструментов для этого, поэтому классы Joda были разработаны. Все классные дети в обработке даты/времени используют их; но, с другой стороны, это включает загрузку и управление сторонней библиотекой.

Ответ 6

notnoop ответ определенно правильный. Однако, если вы собираетесь делать много обработки дат, времени и интервалов, я предлагаю вам взглянуть на класс DateUtils в apache commons lang и в joda-time.

JDK7 придет с лучшей поддержкой некоторых функций, которые предоставляет joda-time. Просто сказать... это может быть связано с тем, что ваше приложение сильно использует этот материал.

Ответ 7

Вам нужно определить свой EPOCH. Эпоха Java (например, Unix) - 1 января 1970 г. /UTC. Я предполагаю, что вы думаете, что добавляете десять месяцев, 3 дня и несколько нечетных часов с 1 января 0000 года, но у вас есть смещение эпохи до 1970 года. Математика может не работать.

Используйте Календарь или Joda (как упоминалось). Если вы просто хотите добавить несколько секунд и дней (& c), не стесняйтесь добавлять указанное количество миллисекунд к вашему первому объекту даты.

Ответ 8

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

Calendar calendar=Calendar.getInstance();

calendar.add(Calendar.Date,23);

calendar.add(Calendar.Month,13);

calendar.add(Calendar.Year,15);

Используя метод add в классе Calendar, мы можем добавить день, месяц, год к существующей дате.

нажмите здесь для полной программы.

Ответ 9

Мое решение добавить время к дате:

private Date getMergedDateTime(Date p_date, Date p_time)
{
    SimpleDateFormat sdfDate = new SimpleDateFormat("dd.MM.yyyy");
    SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm");

    Date merged = null;
    try {
        merged = new SimpleDateFormat("dd.MM.yyyy HH:mm").parse(sdfDate.format(p_date.getTime()) +" " + sdfTime.format(p_time.getTime()));
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return merged;
}

Ответ 10

Иногда я тоже виноват в этой практике, сохраняя временной интервал в объекте даты и используя getTime(), как было предложено notnoop.

Это работает. Вопреки определенному мнению, это, безусловно, работает. Я просто игнорирую, что интервал может быть репрезентативным для непреднамеренной даты. Для меня быстрый и грязный способ добавить интервал, скажем, [6 лет, 6 месяцев, 6 дней, 6 часов, 6 минут, 6 секунд] до даты.