How to "Pretty Print" a Duration in Java

How can I pretty print a Duration in Java?

Joda Time has a pretty good way to do this using a PeriodFormatterBuilder.

Quick Win: PeriodFormat.getDefault().print(duration.toPeriod());

e.g.

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

Duration duration = new Duration(123456); // in milliseconds
PeriodFormatter formatter = new PeriodFormatterBuilder()
.appendDays()
.appendSuffix("d")
.appendHours()
.appendSuffix("h")
.appendMinutes()
.appendSuffix("m")
.appendSeconds()
.appendSuffix("s")
.toFormatter();
String formatted = formatter.print(duration.toPeriod());
System.out.println(formatted);

Pretty print java.time.Duration to something like 2 days

I ended up using this (extended from this answer https://stackoverflow.com/a/49628638/5300930).

I'm sure that there should be a simpler way to do this using the Java/Scala libraries? Note, my input will always be a java.time.Duration, I can use the Joda libraries, but they must be able to start with a java.time.Duration as input.

  /** Encodes a [[java.time.Duration]] into a more readable format.
*
* @example
* encodeDuration(Duration.ofDays(30).plusHours(3)) // returns "30 days, 3 hours"
* encodeDuration(Duration.ofDays(1)) // returns "1 day"
* encodeDuration(Duration.parse("PT720H")) // returns "30 days"
* encodeDuration(Duration.parse("PT12000H10S")) // returns "1 year, 4 months, 15 days, 10 seconds"
*/
implicit val encodeDuration: Encoder[java.time.Duration] = (duration: Duration) => {
val oneMinute = 60
val twoMinutes = oneMinute * 2
val oneHour = oneMinute * 60
val twoHours = oneHour * 2
val oneDay = oneHour * 24
val twoDays = oneDay * 2
val oneMonth = oneDay * 30
val twoMonths = oneMonth * 2
val oneYear = oneDay * 365
val twoYears = oneYear * 2

// scalastyle:off cyclomatic.complexity
def encodeDuration(result: List[String], seconds: Long): List[String] = {

seconds match {
case seconds if seconds <= 0 =>
List.empty[String]
case seconds if seconds == 1 =>
result ::: List(s"${seconds} second")
case seconds if seconds < oneMinute =>
result ::: List(s"${seconds} seconds")
case seconds if seconds >= oneMinute && seconds < twoMinutes =>
List(s"${seconds / oneMinute} minute") ::: encodeDuration(result, seconds % oneMinute)
case seconds if seconds >= oneMinute && seconds < oneHour =>
List(s"${seconds / oneMinute} minutes") ::: encodeDuration(result, seconds % oneMinute)
case seconds if seconds >= oneHour && seconds < twoHours =>
List(s"${seconds / oneHour} hour") ::: encodeDuration(result, seconds % oneHour)
case seconds if seconds >= twoHours && seconds < oneDay =>
List(s"${seconds / oneHour} hours") ::: encodeDuration(result, seconds % oneHour)
case seconds if seconds >= oneDay && seconds < twoDays =>
List(s"${seconds / oneDay} day") ::: encodeDuration(result, seconds % oneDay)
case seconds if seconds >= twoDays && seconds < oneMonth =>
List(s"${seconds / oneDay} days") ::: encodeDuration(result, seconds % oneDay)
case seconds if seconds >= oneMonth && seconds < twoMonths =>
List(s"${seconds / oneMonth} month") ::: encodeDuration(result, seconds % oneMonth)
case seconds if seconds >= twoMonths && seconds < oneYear =>
List(s"${seconds / oneMonth} months") ::: encodeDuration(result, seconds % oneMonth)
case seconds if seconds >= oneYear && seconds < twoYears =>
List(s"${seconds / oneYear} year") ::: encodeDuration(result, seconds % oneYear)
case seconds if seconds >= twoYears =>
List(s"${seconds / oneYear} years") ::: encodeDuration(result, seconds % oneYear)
}
}
// scalastyle:on cyclomatic.complexity

Encoder.encodeString(encodeDuration(List.empty[String], duration.getSeconds).mkString(", "))
}

Print a Duration in human readable format by EL

Unfortunately there exists no elegant builtin way to format a Duration in Java 8. The best i have found is to use the method bobince describes in this answer:

    Duration duration = Duration.ofHours(1).plusMinutes(20);
long s = duration.getSeconds();
System.out.println(String.format("%d:%02d:%02d", s/3600, (s%3600)/60, (s%60)));

Which prints:

1:20:00

The code will have to be tuned if you need longer durations.

I'm not sure what you mean that you are missing a convert method, but Duration is well suited for adding/subtracting on LocalTime. The methods LocalTime.plus() and LocalTime.minus() accepts Duration as argument.

How to format a duration in java? (e.g format H:MM:SS)

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.

Java pretty print for duration

With JodaTime:

PeriodFormat.getDefault( ).print( Hours.THREE );
PeriodFormat.getDefault( ).print( Seconds.seconds( 25 ) );
PeriodFormat.getDefault( ).print( Minutes.minutes( 20 ) );

P.S. Also it's very easy to get the number of hours/seconds/minutes between 2 time points.

Formatting a Duration in Java 8 / jsr310

There is no period/duration-formatter in jsr-310, different from JodaTime. Not every feature of JodaTime was ported to JSR-310 (for example also not PeriodType). And in reverse JSR-310 has some features which are not available in JodaTime (for example localized weekday numbers or the strategy pattern approach with adjusters).

It might happen that Java 9 will introduce some kind of built-in period formatting (read something about this from S. Colebourne).

Conclusion: JSR-310 and JodaTime are not fully compatible to each other, so a lot of work can be required. I would not be so keen on migration as soon as possible. Do you need special features of JSR-310 which are not offered by JodaTime?

Additional note: You should also be aware of the fact that joda period (which includes all units from years to seconds) is not fully compatible with jsr310-period (only years, months, days) or jsr310-duration (only hours, minutes, seconds and fraction seconds).

Most idiomatic way to print a time difference in Java?

Apache Commons has the DurationFormatUtils class for applying a specified format to a time duration. So, something like:

long time = System.currentTimeMillis();
//do something that takes some time...
long completedIn = System.currentTimeMillis() - time;

DurationFormatUtils.formatDuration(completedIn, "HH:mm:ss:SS");

How to format a Period in Java 8 / jsr310?

One solution is to simply use String.format:

import java.time.Period;

Period p = Period.of(2,5,1);
String.format("%d years, %d months, %d days", p.getYears(), p.getMonths(), p.getDays());

If your really need to use the features of DateTimeFormatter, you can use a temporary LocalDate, but this is a kind of hack that distort the semantic of LocalDate.

import java.time.Period;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

Period p = Period.of(2,5,1);
DateTimeFormatter fomatter = DateTimeFormatter.ofPattern("y 'years,' M 'months,' d 'days'");
LocalDate.of(p.getYears(), p.getMonths(), p.getDays()).format(fomatter);


Related Topics



Leave a reply



Submit