Does anyone know of a Java library that can pretty print a number in milliseconds in the same way that C# does?
E.g., 123456 ms as a long would be printed as 4d1h3m5s.
Does anyone know of a Java library that can pretty print a number in milliseconds in the same way that C# does?
E.g., 123456 ms as a long would be printed as 4d1h3m5s.
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);
PeriodType.daysTime()
or .standard()
didn't help –
Strachan I've built a simple solution, using Java 8's Duration.toString()
and a bit of regex:
public static String humanReadableFormat(Duration duration) {
return duration.toString()
.substring(2)
.replaceAll("(\\d[HMS])(?!$)", "$1 ")
.toLowerCase();
}
The result will look like:
- 5h
- 7h 15m
- 6h 50m 15s
- 2h 5s
- 0.1s
If you don't want spaces between, just remove replaceAll
.
Duration::toString
is formatted according to the well-defined and well-worn ISO 8601 standard. –
Longitudinal .replaceAll("\\.\\d+", "")
before the call to toLowerCase()
. –
Liquidator Float#toString()
changed from 4 to 3 digits. This prevented the company I worked for years to upgrade Java. Of course you can use toString(), but for other things than debugging or logging it is a bad architectural choice and should be avoided IMHO. –
Sutherlan 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);
PeriodType.daysTime()
or .standard()
didn't help –
Strachan Apache commons-lang provides the DurationFormatUtils class to get this done as well. It has several formatting methods like formatDurationHMS
and formatDurationISO
, but if you just want a nice output, use:
DurationFormatUtils.formatDurationWords(123456, true, true);
Result
2 minutes 3 seconds
Java 9+
Duration d1 = Duration.ofDays(0);
d1 = d1.plusHours(47);
d1 = d1.plusMinutes(124);
d1 = d1.plusSeconds(124);
System.out.println(String.format("%s d %sh %sm %ss",
d1.toDaysPart(),
d1.toHoursPart(),
d1.toMinutesPart(),
d1.toSecondsPart()));
2 d 1h 6m 4s
org.threeten.extra.AmountFormats.wordBased
The ThreeTen-Extra project, which is maintained by Stephen Colebourne, the author of JSR 310, java.time, and Joda-Time, has an AmountFormats
class which works with the standard Java 8 date time classes. It's fairly verbose though, with no option for more compact output.
Duration d = Duration.ofMinutes(1).plusSeconds(9).plusMillis(86);
System.out.println(AmountFormats.wordBased(d, Locale.getDefault()));
1 minute, 9 seconds and 86 milliseconds
wordbased
ResourceBundle, and overriding the WordBased.spaceandspace
key. You could do this by putting a file named org/threeten/extra/wordbased_ox.properties
on your ClassPath with contents WordBased.spaceandspace=, and
. Then, if you use Locale.forLanguageTag("ox")
the Oxford comma will be added automatically. Source is here: github.com/ThreeTen/threeten-extra/blob/master/src/main/… –
Phallicism Another Java 9+ solution:
private String formatDuration(Duration duration) {
List<String> parts = new ArrayList<>();
long days = duration.toDaysPart();
if (days > 0) {
parts.add(plural(days, "day"));
}
int hours = duration.toHoursPart();
if (hours > 0 || !parts.isEmpty()) {
parts.add(plural(hours, "hour"));
}
int minutes = duration.toMinutesPart();
if (minutes > 0 || !parts.isEmpty()) {
parts.add(plural(minutes, "minute"));
}
int seconds = duration.toSecondsPart();
parts.add(plural(seconds, "second"));
return String.join(", ", parts);
}
private String plural(long num, String unit) {
return num + " " + unit + (num == 1 ? "" : "s");
}
E.g.,
6 days, 21 hours, 2 minutes, 14 seconds
2 years 11 months 28 days
–
Wiggins A Java 8 version based on user678573's answer:
private static String humanReadableFormat(Duration duration) {
return String.format("%s days and %sh %sm %ss", duration.toDays(),
duration.toHours() - TimeUnit.DAYS.toHours(duration.toDays()),
duration.toMinutes() - TimeUnit.HOURS.toMinutes(duration.toHours()),
duration.getSeconds() - TimeUnit.MINUTES.toSeconds(duration.toMinutes()));
}
... since there is no PeriodFormatter in Java 8 and no methods like getHours, getMinutes, ...
I'd be happy to see a better version for Java 8.
2 years 11 months 28 days
–
Wiggins JodaTime has a Period
class that can represent such quantities, and can be rendered (via IsoPeriodFormat
) in ISO8601 format, e.g. PT4D1H3M5S
, e.g.
Period period = new Period(millis);
String formatted = ISOPeriodFormat.standard().print(period);
If that format isn't the one you want, then PeriodFormatterBuilder
lets you assemble arbitrary layouts, including your C#-style 4d1h3m5s
.
new Period(millis).toString()
uses the ISOPeriodFormat.standard()
by default. –
Wandering Here's how you can do it using pure JDK code:
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
long diffTime = 215081000L;
Duration duration = DatatypeFactory.newInstance().newDuration(diffTime);
System.out.printf("%02d:%02d:%02d", duration.getDays() * 24 + duration.getHours(), duration.getMinutes(), duration.getSeconds());
An alternative to the builder-approach of Joda-Time would be a pattern-based solution. This is offered by my library Time4J. Example using the class Duration.Formatter (added some spaces for more readability - removing the spaces will yield the wished C#-style):
IsoUnit unit = ClockUnit.MILLIS;
Duration<IsoUnit> dur = // normalized duration with multiple components
Duration.of(123456, unit).with(Duration.STD_PERIOD);
Duration.Formatter<IsoUnit> f = // create formatter/parser with optional millis
Duration.Formatter.ofPattern("D'd' h'h' m'm' s[.fff]'s'");
System.out.println(f.format(dur)); // output: 0d 0h 2m 3.456s
This formatter can also print durations of java.time
-API (however, the normalization features of that type are less powerful):
System.out.println(f.format(java.time.Duration.ofMillis(123456))); // output: 0d 0h 2m 3.456s
The expectation of the OP that "123456 ms as a long would be printed as 4d1h3m5s" is calculated in an obviously wrong way. I assume sloppiness as reason. The same duration formatter defined above can also be used as parser. The following code shows that "4d1h3m5s" rather corresponds to 349385000 = 1000 * (4 * 86400 + 1 * 3600 + 3 * 60 + 5)
:
System.out.println(
f.parse("4d 1h 3m 5s")
.toClockPeriodWithDaysAs24Hours()
.with(unit.only())
.getPartialAmount(unit)); // 349385000
Another way is using the class net.time4j.PrettyTime
(which is also good for localized output and printing relative times like "yesterday", "next Sunday", "4 days ago" etc.):
String s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.NARROW);
System.out.println(s); // output: 2m 3s 456ms
s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds, and 456 milliseconds
s = PrettyTime.of(Locale.UK).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds and 456 milliseconds
The text width controls if abbreviations are used or not. The list format can be controlled, too, by choosing the appropriate locale. For example, standard English uses the Oxform comma, while UK does not. The latest version v5.5 of Time4J supports more than 90 languages and uses translations based on the CLDR-repository (an industry standard).
With Java 8 you can also use the toString()
method of java.time.Duration
to format it without external libraries using ISO 8601 seconds based representation such as PT8H6M12.345S.
I realize this might not fit your use case exactly, but PrettyTime might be useful here.
PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
//prints: “right now”
System.out.println(p.format(new Date(1000*60*10)));
//prints: “10 minutes from now”
naive kotlin version (avoiding to*Part
functions, to work with Android)
fun prettyFormatSecondsToDuration(seconds: Long): String {
val duration = java.time.Duration.ofSeconds(seconds)
return buildString {
val hours = duration.toHours()
if (hours > 0) append("${hours}h ")
val minutes = duration.minusHours(hours).toMinutes()
if (minutes > 0) append("${minutes}m ")
val partSeconds = duration.minusHours(hours).minusMinutes(minutes).seconds
if (partSeconds > 0) append("${partSeconds}s ")
}.trimEnd()
}
toXxxPart
methods are available on Android through desugaring. –
Unfasten © 2022 - 2024 — McMap. All rights reserved.
Duration
defined in the sensible standard, ISO 8601:PnYnMnDTnHnMnS
whereP
means "Period" and marks the beginning,T
separates the date portion from time portion, and in between are optional occurrances of a number with a single-letter abbreviation. For example,PT4H30M
= four and a half hours. – Longitudinal%
and/
to split the number into parts. Almost easier than some of the proposed answers. – Shandy/
and%
. – Unfasten