First, we have to understand the difference between times and durations:
- one timetable represents a specific moment of the day. Ex: the meeting will be at two o'clock in the afternoon (it says exactly at what time of the day the meeting will be)
- one duration represents a amount of time. Ex: the meeting lasted two hours (I didn’t say what time she started or finished, just said how long she lasted)
What can be confusing is that both times and durations use the same words (hours, minutes and seconds), and are often written the same ("11:00" can be either 11:00 in the morning or an 11-hour duration). But each represents a different concept.
What happens is that java.util.Date
, java.util.Calendar
and java.text.SimpleDateFormat
are used 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 timetable:
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Date date = sdf.parse("11:00:00");
System.out.println(date);
This code prints out:
Thu Jan 01 11:00:00 BRT 1970
Remembering that the output may vary, since when printing a Date
, is used the time zone corresponding to Timezone default 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 "1 January 1970, at 11 am". That’s a date and time, not a duration. You could even use the value of date.getTime()
and correct according to the offset of Timezone (as you did in his reply), but the truth is that this solution is not ideal, since you are treating dates and times as if they were durations. You’re basically using a screwdriver to hammer a nail (it might even "work", but it’s 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 suggested to reply from @jpkrohling: manipulate the strings and do the accounts 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 this 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 doesn’t provide a mechanism to properly format a duration, so the way is to do the accounts manually, in a similar way 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
, for it is the total of minutes corresponding to the total duration (corresponding 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 accounts manually.
Java >= 9
Since Java 9, methods such as toMinutesPart()
, that already bring these values properly "broken". That is, for Java >= 9, the final part (after the 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 this case, I also needed to take 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 little boring detail, but still, much better than doing the math manually.
Java 6 and 7
For Java 6 and 7, the solution is to do the accounts manually, as already suggested. But there is also an alternative: use the Threeten Backport, one backport of java.time
, which has basically the same classes and functionalities as Java 8 (not 100%, of course, but the main classes and methods are available).
The code is the same as the previous Java 8 example, the difference is that instead of the package java.time
, the package is used org.threeten.bp
.
Java 5
Finally, for Java 5 (in addition to the first solution of doing the accounts manually), an alternative is to use the Joda-Time. This library includes the class org.joda.time.format.PeriodFormatter
to do the Parsing and formatting of 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 project "terminated" and on its own site there’s a warning on this, recommending migration to the java.time
. Anyway, for those who are still "stuck" to Java 5, it is a great alternative to Date
and Calendar
.
Note also that the class Duration
is in the package org.joda.time
(not to be confused with the java.time.Duration
Java >= 8). Joda-Time is not 100% identical to java.time
, but many of its concepts and ideas have been taken advantage of in Java 8 (including some classes and methods have the same names). The main similarities and differences between Apis are explained here and here.
Do not confuse dates/times with durations
One of the answers are using java.time.LocalTime
to represent 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
Summing up a duration of 12 hours with another of 17 hours, the result should be a total of 29 hours. But how LocalTime
represents a time (not a duration), it only supports values up to 23:59:59.999999999
, and after that she "comes back" to midnight. So the result is 05:00
(5 am - again, a time, not a duration).
It’s the same story as Date
and SimpleDateFormat
: Using classes that represent dates and times to work with durations will not always work. Often it will be just by coincidence, and yet for limited cases.
If you already have available the classes of java.time
, use the right types for each case. If you need to deal with schedules, use LocalTime
. If you need to deal with durations, use Duration
(or Period
).
In total should opinion 2:00:00, which in case is the interval between 11:00 and 13:00? From what I understand it is adding up 11 + 12 + 13 = 36, you need only display the interval between the first time and the last right?
– noNihongo
@Eduardobentorochajunior Actually I don’t need the difference no, I need the same sum.
– Philippe Gioseffi