How to add times in Java?

I am working on a report that does a sum of the data and part of the data is times, example:

 -----------------------
| Atividade |   Tempo   |
 -----------------------
|    1      |  11:00:00 |
 -----------------------
|    2      |  12:00:00 |
 -----------------------
|    3      |  13:00:00 |
 -----------------------
| Total     |  36:00:00 |
 -----------------------

I'm trying to sum the times like this:

final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
final Calendar c = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
c.setTimeInMillis(0);
for (final String h : listaHoras) {
    c.add(Calendar.MILLISECOND, (int) dt.parse(h).getTime());
}

The variable listaHoras is a ArrayList of String ' s all in the format respected by SimpleDateFormat. The problem of the above code is that I can not generate the summed value, using the same SimpleDateFormat it returns me the time of a day there in front according to the date, but within 24 hour.

Does anyone know how to solve this problem?

 27
Author: hkotsubo, 2014-03-06

4 answers

The easiest and most convenient is to use the Duration of JodaTime, but maybe it doesn't make much sense to add this dependency if you need to calculate dates only in this part.

Unfortunately, the easiest way to do this using only the native libraries is similar to what you've done before, but instead of using a Calendar, manually calculate the total.

For example:

// estes numeros nem sempre sao verdade, mas deve ser suficiente para seu caso
long SECOND = 1000;
long MINUTE = 60 * SECOND;
long HOUR = 60 * MINUTE;
long DAY = 24 * HOUR;

// somatoria dos getTime, semelhante ao codigo que ja tens
long elapsedTimeInMilliseconds = ...

double elapsedDays = Math.floor(elapsedTimeInMilliseconds / DAY);
long remaining = elapsedTimeInMilliseconds % DAY; // resto da divisao acima
double elapsedHours = Math.floor(remaining / HOUR);
remaining = remaining % HOUR;
// e por ai vai, ate chegar nos segundos (se for o caso)    

EDIT: This answer was perfectly valid in 2014. Today, a API very similar to that of JodaTime is available in Java if , therefore, it makes no sense to do this calculation manually if the platform where the code is running supports Java 8.

 21
Author: jpkrohling, 2016-03-30 08:17:10

I got to this code after Question in the OS and will post for possible criticism.

public static void somaTempos(final String[] listaTempos) throws ParseException {
    long tm = 0;
    final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
    final Calendar c = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
    for (String tmp : listaTempos) {
        c.setTime(dt.parse(tmp));
        tm += c.get(Calendar.SECOND) + 60 * c.get(Calendar.MINUTE) + 3600 * c.get(Calendar.HOUR_OF_DAY);
    }

    final long l = tm % 3600;
    System.out.println(SIGRUtil.format(tm / 3600) + ':' + SIGRUtil.format(l / 60) + ':' + SIGRUtil.format(l % 60));
}

private static String format(long s) {
    if (s < 10) {
        return "0" + s;
    }
    return String.valueOf(s);
}

Update

Still working on the solution I developed the following code.

public static String somaTempos(final List<String> listaTempos) {
    long milissegundos = 0;
    final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
    dt.setLenient(false);
    try {
        // Deslocamento de fuso-horário.
        final long timezoneOffset = dt.parse("00:00:00").getTime();
        for (final String tempo : listaTempos) {
            milissegundos += (dt.parse(tempo).getTime() - timezoneOffset);
        }
    } catch (final ParseException e) {
        throw new BusinessException(
                "Lista de tempos deve ser passada com os tempos respeitando o padrão HH:mm:ss.", e);
    }

    ((SimpleDateFormat) dt).applyPattern(":mm:ss");
    return new StringBuilder(8).append(milissegundos / DateUtils.MILLIS_PER_HOUR).append(
            dt.format(new Date(milissegundos))).toString();
}

In fact, the API gives me for free the minutes and seconds after doing the calculation of the sum of the times without forgetting to consider the time zone offset. My real problem was calculating the number of hours on a given date, which was actually the easiest point of the problem and that was the first part I managed to solve.

Code improvement considerations?

 8
Author: Philippe Gioseffi, 2017-05-23 12:37:35

Since it is something specific to hours, you can use LocalTime which provides the method plusHours to perform sum of hours. There are other methods to treat sum of minutes, seconds, etc (TODO: See class documentation).

LocalTime primeiro = LocalTime.of(12, 0); // 12:00
LocalTime segundo  = LocalTime.of(5, 45); //  5:45

LocalTime total = primeiro.plusHours(segundo.getHour())
                          .plusMinutes(segundo.getMinute());

System.out.println(total); // 17:45

Not working IDEONE

If you are working with strings, there is the parse to get an object LocalTime from a string:

LocalTime primeiro = LocalTime.parse("12:00");
LocalTime segundo  = LocalTime.parse("05:45");

LocalTime total = primeiro.plusHours(segundo.getHour())
                          .plusMinutes(segundo.getMinute());

System.out.println(total); // 17:45

Not working IDEONE

 5
Author: Renan Gomes, 2016-12-20 17:52:46

First, we have to understand the difference between times and durations:

  • a time represents a specific time of the day. Ex: the meeting will be at two o'clock in the afternoon (says exactly what time of day the meeting will be)
  • a duration represents an amount of time. Ex: the meeting lasted two hours (I didn't say what time it started or ended, just said how long it lasted)

What it can confuse is that both times and durations use the same words (hours, minutes and seconds), and are often written the same way ("11:00" can be both 11 a.m. and an 11-hour duration). But each represents a different concept.


What happens is that java.util.Date, java.util.Calendar e java.text.SimpleDateFormat they serve to work with dates and times, but not with durations .

In some cases it may even "work", but it will be by coincidence. For example, when doing parsing of 11:00:00 as if it were a time:

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Date date = sdf.parse("11:00:00");
System.out.println(date);

This code prints:

Thu Jan 01 11: 00: 00 BRT 1970

remembering that the output can vary, since when printing a Date, it is used the time zone corresponding to the timezone default of the JVM (in the case, the "BRT" indicates the time of Brasilia, since in my JVM the timezone default is America/Sao_Paulo).

Note that the resulting date is"January 1, 1970, 11 a.m.". this is a date and time, not a duration . You could even use the value of date.getTime() and correct according to the timezone's offset (as you did in your answer), but the truth is that this solution is not ideal, since you are treating dates and times as if they were durations. You are basically using a screwdriver to hammer a nail (can even "work", but not the ideal way).

Unfortunately the legacy API (Date, Calendar and SimpleDateFormat) has no mechanism for working with durations (including, was one of the many reasons that led to the creation of the Java 8 API ). The solution, in this case, is to do as @jpkrohling's answer suggested: manipulate the strings and do the counts manually.

List<String> listaTempos = Arrays.asList("11:00:00", "12:00:00", "13:00:00");
long totalSegundos = 0;
for (String tempo : listaTempos) {
    String[] partes = tempo.split(":");
    long horas = Long.parseLong(partes[0]);
    long minutos = Long.parseLong(partes[1]);
    long segundos = Long.parseLong(partes[2]);
    totalSegundos += segundos + (minutos * 60) + (horas * 3600);
}
long totalHoras = totalSegundos / 3600;
totalSegundos -= (totalHoras * 3600);
long totalMinutos = totalSegundos / 60;
totalSegundos -= (totalMinutos * 60);
String totalFormatado = String.format("%02d:%02d:%02d", totalHoras, totalMinutos, totalSegundos);
System.out.println(totalFormatado); // 36:00:00

Java > = 8

From Java 8 you can use the API java.time. In addition to much higher than Date and Calendar, it also has specific classes to handle durations. In the case, we can use a java.time.Duration:

List<String> listaTempos = Arrays.asList("11:00:00", "12:00:00", "13:00:00");
Duration total = Duration.ZERO;
for (String tempo : listaTempos) {
    String[] partes = tempo.split(":");
    total = total
        // somar horas
        .plusHours(Long.parseLong(partes[0]))
        // somar minutos
        .plusMinutes(Long.parseLong(partes[1]))
        // somar segundos
        .plusSeconds(Long.parseLong(partes[2]));
}

With this, the variable total will represent a Duration containing the total duration. Unfortunately the API still does not provide a mechanism to properly format a duration, so the way is to do the accounts manually, similar to the previous solution:

long totalSegundos = total.getSeconds();
long totalHoras = totalSegundos / 3600;
totalSegundos -= (totalHoras * 3600);
long totalMinutos = totalSegundos / 60;
totalSegundos -= (totalMinutos * 60);
String totalFormatado = String.format("%02d:%02d:%02d", totalHoras, totalMinutos, totalSegundos);
System.out.println(totalFormatado); // 36:00:00

Just one detail: the class Duration has the method toMinutes(), that apparently could be used. But in this case it will return 2160, since it is the total of minutes corresponding to the total duration (which corresponds to 36 hours). To get the duration "broken" in hours, minutes and seconds, the only solution - at least in Java 8-is to do the counts manually.

Java > = 9

From Java 9 methods such astoMinutesPart(), that already bring these values properly "broken". That is, for Java > = 9, the final part (after for) would be:

for (String tempo: listaTempos) {
    ... // igual ao exemplo anterior
}
String totalFormatado = String.format("%02d:%02d:%02d", (total.toDaysPart() * 24) + total.toHoursPart(),
                                      total.toMinutesPart(), total.toSecondsPart());
System.out.println(totalFormatado); // 36:00:00

In the case, I needed to take also the value of toDaysPart(), since a duration of 36 hours is returned as "1 day and 12 hours" by the respective methods toDaysPart() and toHoursPart(). A bit boring detail, but still, much better than doing the beads manually.

Java 6 and 7

For Java 6 and 7, the solution is to do the counts manually, as already suggested. But there is also an alternative: use the ThreeTen backport , a backport of the java.time, which has basically the same classes and functionality as Java 8 (not 100%, of course, but the main classes and methods are available).

The code is the same as the previous example of Java 8, the difference is that instead of the package java.time, the package org.threeten.bp is used.

Java 5

Finally, for Java 5 (in addition to the first solution of doing the accounts manually), a alternative is to use the Joda-Time. This library even contains the class org.joda.time.format.PeriodFormatter to do parsing and formatting a duration:

import org.joda.time.Duration;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;

List<String> listaTempos = Arrays.asList("11:00:00", "12:00:00", "13:00:00");
PeriodFormatter fmt = new PeriodFormatterBuilder()
    .printZeroAlways().minimumPrintedDigits(2)
    .appendHours().appendSeparator(":")
    .appendMinutes().appendSeparator(":")
    .appendSeconds().toFormatter();
Duration total = Duration.ZERO;
for (String tempo : listaTempos) {
    total = total.plus(fmt.parsePeriod(tempo).toStandardDuration());
}
String totalFormatado = fmt.print(total.toPeriod());
System.out.println(totalFormatado); // 36:00:00

Remembering that Joda-Time is a "terminated" project and on its own website there is a warning about this, recommending the migration to java.time. Anyway, for those who are still "stuck" with Java 5, it's a great alternative to Date and Calendar.

Also Note that the class Duration is in the package org.joda.time (not to be confused with Java's java.time.Duration > = 8). Joda-Time is not 100% identical to java.time, but many of its concepts and ideas have been leveraged in Java 8 (including some classes and methods have the same names). The main similarities and differences between the APIs are explained here and here.


Do not confuse dates / times with durations

One of the answers is using java.time.LocalTime to represent the durations. But this class represents a time, not a duration. This means that it only works if the total is less than 23 hours and 59 minutes. Ex:

LocalTime primeiro = LocalTime.parse("12:00");
LocalTime segundo = LocalTime.parse("17:00");
LocalTime total = primeiro.plusHours(segundo.getHour()).plusMinutes(segundo.getMinute());
System.out.println(total); // 05:00

Adding a duration of 12 hours with another of 17 hours, the result should be a total of 29 hours. But since LocalTime represents a time (and not a duration), it only supports values until 23:59:59.999999999, and after that it "goes back" to midnight. So the result is 05:00 (5 am-again, a time, not a duration).

Is the same story as Date and SimpleDateFormat: using classes that represent dates and times to work with durations won't always work. Often it will be only by coincidence, and even then for limited cases.

If you already have java.time classes available, use the correct types for each case. If you need to handle schedules, use LocalTime. If you need to handle durations, use Duration (or Period).

 2
Author: hkotsubo, 2019-04-09 13:02:52