I'd like to format a duration in seconds using a pattern like H:MM:SS. The current utilities in java are designed to format a time but not a duration.
I'd like to format a duration in seconds using a pattern like H:MM:SS. The current utilities in java are designed to format a time but not a duration.
If you don't want to drag in libraries, it's simple enough to do yourself using a Formatter, or related shortcut eg. given integer number of seconds s:
String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
If you're using a version of Java prior to 8... you can use Joda Time and PeriodFormatter. If you've really got a duration (i.e. an elapsed amount of time, with no reference to a calendar system) then you should probably be using Duration for the most part - you can then call toPeriod (specifying whatever PeriodType you want to reflect whether 25 hours becomes 1 day and 1 hour or not, etc) to get a Period which you can format.
If you're using Java 8 or later: I'd normally suggest using java.time.Duration to represent the duration. You can then call getSeconds() or the like to obtain an integer for standard string formatting as per bobince's answer if you need to - although you should be careful of the situation where the duration is negative, as you probably want a single negative sign in the output string. So something like:
public static String formatDuration(Duration duration) {
long seconds = duration.getSeconds();
long absSeconds = Math.abs(seconds);
String positive = String.format(
"%d:%02d:%02d",
absSeconds / 3600,
(absSeconds % 3600) / 60,
absSeconds % 60);
return seconds < 0 ? "-" + positive : positive;
}
Formatting this way is reasonably simple, if annoyingly manual. For parsing it becomes a harder matter in general... You could still use Joda Time even with Java 8 if you want to, of course.
There's a fairly simple and (IMO) elegant approach, at least for durations of less than 24 hours:
DateTimeFormatter.ISO_LOCAL_TIME.format(value.addTo(LocalTime.of(0, 0)))
Formatters need a temporal object to format, so you can create one by adding the duration to a LocalTime of 00:00 (i.e. midnight). This will give you a LocalTime representing the duration from midnight to that time, which is then easy to format in standard HH:mm:ss notation. This has the advantage of not needing an external library, and uses the java.time library to do the calculation, rather than manually calculating the hours, minutes and seconds.
This answer only uses Duration methods and works with Java 8 :
public static String format(Duration d) {
long days = d.toDays();
d = d.minusDays(days);
long hours = d.toHours();
d = d.minusHours(hours);
long minutes = d.toMinutes();
d = d.minusMinutes(minutes);
long seconds = d.getSeconds() ;
return
(days == 0?"":days+" jours,")+
(hours == 0?"":hours+" heures,")+
(minutes == 0?"":minutes+" minutes,")+
(seconds == 0?"":seconds+" secondes,");
}
I'm not sure that is you want, but check this Android helper class
import android.text.format.DateUtils
For example: DateUtils.formatElapsedTime()
You can use java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.
Demo:
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
public class Main {
public static void main(String[] args) {
LocalDateTime startDateTime = LocalDateTime.of(2020, Month.DECEMBER, 10, 15, 20, 25);
LocalDateTime endDateTime = LocalDateTime.of(2020, Month.DECEMBER, 10, 18, 24, 30);
Duration duration = Duration.between(startDateTime, endDateTime);
// Default format
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format("%02d:%02d:%02d", duration.toHours() % 24,
duration.toMinutes() % 60, duration.toSeconds() % 60);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%02d:%02d:%02d", duration.toHoursPart(), duration.toMinutesPart(),
duration.toSecondsPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT3H4M5S
03:04:05
03:04:05
Learn about the modern date-time API from Trail: Date Time.
There is yet another way to make it for java8. But it works if duration is no longer than 24 hours
public String formatDuration(Duration duration) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm.SSS");
return LocalTime.ofNanoOfDay(duration.toNanos()).format(formatter);
}
using this func
private static String strDuration(long duration) {
int ms, s, m, h, d;
double dec;
double time = duration * 1.0;
time = (time / 1000.0);
dec = time % 1;
time = time - dec;
ms = (int)(dec * 1000);
time = (time / 60.0);
dec = time % 1;
time = time - dec;
s = (int)(dec * 60);
time = (time / 60.0);
dec = time % 1;
time = time - dec;
m = (int)(dec * 60);
time = (time / 24.0);
dec = time % 1;
time = time - dec;
h = (int)(dec * 24);
d = (int)time;
return (String.format("%d d - %02d:%02d:%02d.%03d", d, h, m, s, ms));
}
Looking at all those calculations, it may is helpful that most of the Units (Hours, Minutes, etc.) have a .toFooPart() convenience method.
E.g.
Duration.ofMinutes(110L).toMinutesPart() == 50
Read: the minutes elapsed to the next value of the parent unit (Hour).
Here's a one-liner for converting a java.time.Duration to a nice string in Kotlin:
duration.run {
"%d:%02d:%02d.%03d".format(toHours(), toMinutesPart(), toSecondsPart(), toMillisPart())
}
Example output:
120:56:03.004
In scala (I saw some other attempts, and wasn't impressed):
def formatDuration(duration: Duration): String = {
import duration._ // get access to all the members ;)
f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
}
Looks horrible yes? Well that's why we use IDEs to write this stuff so that the method calls ($toHoursPart etc) are a different color.
The f"..." is a printf/String.format style string interpolator (which is what allows the $ code injection to work)
Given an output of 1 14:06:32.583, the f interpolated string would be equivalent to String.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)
In Scala, building up on YourBestBet's solution but simplified:
def prettyDuration(seconds: Long): List[String] = seconds match {
case t if t < 60 => List(s"${t} seconds")
case t if t < 3600 => s"${t / 60} minutes" :: prettyDuration(t % 60)
case t if t < 3600*24 => s"${t / 3600} hours" :: prettyDuration(t % 3600)
case t => s"${t / (3600*24)} days" :: prettyDuration(t % (3600*24))
}
val dur = prettyDuration(12345).mkString(", ") // => 3 hours, 25 minutes, 45 seconds