Reputation:
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.
Upvotes: 239
Views: 255709
Reputation: 11
Like this...
public String getAsString(Duration duration) {
if (duration == null) {
duration = Duration.ZERO;
}
String str = String.format(
"%d:%02d:%02d",
duration.toHoursPart(),
duration.toMinutesPart(),
duration.toSecondsPart()
);
return str;
}
Or like this:
public String getAsString(Duration duration) {
if (duration == null) {
duration = Duration.ZERO;
}
String str = String.format(
"%d:%02d:%02d",
duration.toHours(),
duration.toMinutes() - (duration.toHours() * 60),
duration.toSeconds() - (duration.toMinutes() * 60)
);
return str;
}
Upvotes: 0
Reputation: 86203
This is easier since Java 9. A Duration
still isn’t formattable, but methods for getting the hours, minutes and seconds are added, which makes the task somewhat more straightforward:
LocalDateTime start = LocalDateTime.of(2019, Month.JANUARY, 17, 15, 24, 12);
LocalDateTime end = LocalDateTime.of(2019, Month.JANUARY, 18, 15, 43, 33);
Duration diff = Duration.between(start, end);
String hms = String.format("%d:%02d:%02d",
diff.toHours(),
diff.toMinutesPart(),
diff.toSecondsPart());
System.out.println(hms);
The output from this snippet is:
24:19:21
If you have got your duration as a number of seconds, first convert to a Duration
object:
Duration diff = Duration.ofSeconds(7239);
(You may want another variable name depending on your situation; I reused diff
form above so you can just paste this line into that snippet.)
Upvotes: 83
Reputation: 79015
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:
public class Main {
public static void main(String[] args) {
LocalDateTime startDateTime = LocalDateTime.of(2020, Month.NOVEMBER, 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("%d:%02d:%02d", duration.toHours(),
duration.toMinutes() % 60, duration.toSeconds() % 60);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%d:%02d:%02d", duration.toHours(), duration.toMinutesPart(),
duration.toSecondsPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT723H4M5S
723:04:05
723:04:05
Important: Note that a Duration
object also has a day part. If you want the day part in your result, you have to use duration.toHours() % 24
in Java 8 API or duration.toHoursPart()
in Java 9 as shown below:
public class Main {
public static void main(String[] args) {
LocalDateTime startDateTime = LocalDateTime.of(2020, Month.NOVEMBER, 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("%d days %d hour(s) %d minute(s) %d second(s)", duration.toDays(), duration.toHours() % 24,
duration.toMinutes() % 60, duration.toSeconds() % 60);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%d days %d hour(s) %d minute(s) %d second(s)", duration.toDaysPart(), duration.toHoursPart(),
duration.toMinutesPart(), duration.toSecondsPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT723H4M5S
30 days 3 hour(s) 4 minute(s) 5 second(s)
30 days 3 hour(s) 4 minute(s) 5 second(s)
Learn about the modern date-time API from Trail: Date Time.
Upvotes: 6
Reputation: 4466
String formatDuration(Temporal from, Temporal to) {
final StringBuilder builder = new StringBuilder();
for (ChronoUnit unit : new ChronoUnit[]{YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS}) {
long amount = unit.between(from, to);
if (amount == 0) {
continue;
}
builder.append(' ')
.append(amount)
.append(' ')
.append(unit.name().toLowerCase());
from = from.plus(amount, unit);
}
return builder.toString().trim();
}
Upvotes: 3
Reputation: 920
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+" days,")+
(hours == 0?"":hours+" hours,")+
(minutes == 0?"":minutes+" minutes,")+
(seconds == 0?"":seconds+" seconds,");
}
Upvotes: 13
Reputation: 6323
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
Upvotes: 4
Reputation: 177
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);
}
Upvotes: 6
Reputation: 7166
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).
Upvotes: 0
Reputation: 327
I'm not sure that is you want, but check this Android helper class
import android.text.format.DateUtils
For example: DateUtils.formatElapsedTime()
android date duration elapsedtime
Upvotes: 12
Reputation: 29
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));
}
Upvotes: 1
Reputation: 19944
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)
Upvotes: 1
Reputation: 615
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.
Upvotes: 16
Reputation: 5568
I use Apache common's DurationFormatUtils like so:
DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
Upvotes: 199
Reputation: 765
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
Upvotes: -4
Reputation: 354
This might be kind of hacky, but it is a good solution if one is bent on accomplishing this using Java 8's java.time
:
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;
public class TemporalDuration implements TemporalAccessor {
private static final Temporal BASE_TEMPORAL = LocalDateTime.of(0, 1, 1, 0, 0);
private final Duration duration;
private final Temporal temporal;
public TemporalDuration(Duration duration) {
this.duration = duration;
this.temporal = duration.addTo(BASE_TEMPORAL);
}
@Override
public boolean isSupported(TemporalField field) {
if(!temporal.isSupported(field)) return false;
long value = temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
return value!=0L;
}
@Override
public long getLong(TemporalField field) {
if(!isSupported(field)) throw new UnsupportedTemporalTypeException(new StringBuilder().append(field.toString()).toString());
return temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
}
public Duration getDuration() {
return duration;
}
@Override
public String toString() {
return dtf.format(this);
}
private static final DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.optionalStart()//second
.optionalStart()//minute
.optionalStart()//hour
.optionalStart()//day
.optionalStart()//month
.optionalStart()//year
.appendValue(ChronoField.YEAR).appendLiteral(" Years ").optionalEnd()
.appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral(" Months ").optionalEnd()
.appendValue(ChronoField.DAY_OF_MONTH).appendLiteral(" Days ").optionalEnd()
.appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(" Hours ").optionalEnd()
.appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(" Minutes ").optionalEnd()
.appendValue(ChronoField.SECOND_OF_MINUTE).appendLiteral(" Seconds").optionalEnd()
.toFormatter();
}
Upvotes: 8
Reputation: 1749
in scala, no library needed:
def prettyDuration(str:List[String],seconds:Long):List[String]={
seconds match {
case t if t < 60 => str:::List(s"${t} seconds")
case t if (t >= 60 && t< 3600 ) => List(s"${t / 60} minutes"):::prettyDuration(str, t%60)
case t if (t >= 3600 && t< 3600*24 ) => List(s"${t / 3600} hours"):::prettyDuration(str, t%3600)
case t if (t>= 3600*24 ) => List(s"${t / (3600*24)} days"):::prettyDuration(str, t%(3600*24))
}
}
val dur = prettyDuration(List.empty[String], 12345).mkString("")
Upvotes: -5
Reputation: 13073
How about the following function, which returns either +H:MM:SS or +H:MM:SS.sss
public static String formatInterval(final long interval, boolean millisecs )
{
final long hr = TimeUnit.MILLISECONDS.toHours(interval);
final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
if( millisecs ) {
return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
} else {
return String.format("%02d:%02d:%02d", hr, min, sec );
}
}
Upvotes: 6
Reputation: 44061
My library Time4J offers a pattern-based solution (similar to Apache DurationFormatUtils
, but more flexible):
Duration<ClockUnit> duration =
Duration.of(-573421, ClockUnit.SECONDS) // input in seconds only
.with(Duration.STD_CLOCK_PERIOD); // performs normalization to h:mm:ss-structure
String fs = Duration.formatter(ClockUnit.class, "+##h:mm:ss").format(duration);
System.out.println(fs); // output => -159:17:01
This code demonstrates the capabilities to handle hour overflow and sign handling, see also the API of duration-formatter based on pattern.
Upvotes: 1
Reputation: 368
Here is one more sample how to format duration. Note that this sample shows both positive and negative duration as positive duration.
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.HOURS;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.time.Duration;
public class DurationSample {
public static void main(String[] args) {
//Let's say duration of 2days 3hours 12minutes and 46seconds
Duration d = Duration.ZERO.plus(2, DAYS).plus(3, HOURS).plus(12, MINUTES).plus(46, SECONDS);
//in case of negative duration
if(d.isNegative()) d = d.negated();
//format DAYS HOURS MINUTES SECONDS
System.out.printf("Total duration is %sdays %shrs %smin %ssec.\n", d.toDays(), d.toHours() % 24, d.toMinutes() % 60, d.getSeconds() % 60);
//or format HOURS MINUTES SECONDS
System.out.printf("Or total duration is %shrs %smin %sec.\n", d.toHours(), d.toMinutes() % 60, d.getSeconds() % 60);
//or format MINUTES SECONDS
System.out.printf("Or total duration is %smin %ssec.\n", d.toMinutes(), d.getSeconds() % 60);
//or format SECONDS only
System.out.printf("Or total duration is %ssec.\n", d.getSeconds());
}
}
Upvotes: 6
Reputation: 1500015
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.
Upvotes: 98
Reputation: 536349
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));
Upvotes: 238
Reputation: 41
This is a working option.
public static String showDuration(LocalTime otherTime){
DateTimeFormatter df = DateTimeFormatter.ISO_LOCAL_TIME;
LocalTime now = LocalTime.now();
System.out.println("now: " + now);
System.out.println("otherTime: " + otherTime);
System.out.println("otherTime: " + otherTime.format(df));
Duration span = Duration.between(otherTime, now);
LocalTime fTime = LocalTime.ofNanoOfDay(span.toNanos());
String output = fTime.format(df);
System.out.println(output);
return output;
}
Call the method with
System.out.println(showDuration(LocalTime.of(9, 30, 0, 0)));
Produces something like:
otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463
Upvotes: 4
Reputation: 317
long duration = 4 * 60 * 60 * 1000;
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());
log.info("Duration: " + sdf.format(new Date(duration - TimeZone.getDefault().getRawOffset())));
Upvotes: 30