Risposte:
Se stai usando una versione di Java precedente alla 8 ... puoi usare Joda Time e PeriodFormatter
. Se hai davvero una durata (cioè un periodo di tempo trascorso, senza alcun riferimento a un sistema di calendario), probabilmente dovresti utilizzare Duration
per la maggior parte - puoi quindi chiamare toPeriod
(specificando qualsiasi cosa PeriodType
tu voglia riflettere se diventa 25 ore 1 giorno e 1 ora o meno, ecc.) Per ottenere un Period
formato che è possibile formattare.
Se stai usando Java 8 o successivo: normalmente suggerirei di usare java.time.Duration
per rappresentare la durata. Puoi quindi chiamare getSeconds()
o simili per ottenere un numero intero per la formattazione di stringhe standard secondo la risposta di bobince se necessario - anche se dovresti fare attenzione alla situazione in cui la durata è negativa, poiché probabilmente vuoi un singolo segno negativo nella stringa di output . Quindi qualcosa del tipo:
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;
}
La formattazione in questo modo è ragionevolmente semplice, anche se fastidiosamente manuale. Per l' analisi diventa una cosa più difficile in generale ... Potresti comunque usare Joda Time anche con Java 8 se vuoi, ovviamente.
Duration
metodi s: duration.toHours()
, duration.toMinutesPart()
e duration.toSecondsPart()
che sono disponibili dal Java 9. E per ottenere il valore assoluto può usare duration.abs()
.
Se non vuoi trascinare le librerie, è abbastanza semplice fare te stesso usando un Formatter o un collegamento correlato, ad es. dato il numero intero di secondi s:
String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
s/86400, (s%86400)/3600...
per giorni se necessario ...
s > 8640
0 (un giorno): "\u221E"
- infinito
Uso DurationFormatUtils di Apache Common in questo modo:
DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
Questo è più facile da Java 9. Un Duration
metodo non è ancora formattabile, ma vengono aggiunti metodi per ottenere ore, minuti e secondi, il che rende l'attività un po 'più semplice:
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);
L'output di questo snippet è:
24:19:21
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())));
sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
prima di utilizzare la format
funzione.
Questo potrebbe essere un po 'confuso, ma è una buona soluzione se si è intenzionati a farlo utilizzando Java 8 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();
}
Duration.ofSeconds(20)
), quindi mi piacerebbe avere una UnsupportedTemporalTypeException
). Ho semplicemente rimosso il codice verificando se la differenza è == 01
. Ho pensato che 01
fosse una sorta di valore di mascheramento che non capisco completamente, puoi spiegarlo?
isSupported
metodo, restituisce le informazioni se esiste un campo valido da mostrare sulla stringa leggibile dall'uomo. Comunque il "mascheramento" non è in realtà una maschera. Il codice mostra di return value!=0l
no return value!=01
. Per favore, usa copia-incolla la prossima volta.
TemporalAccessor
non dovrebbe essere utilizzata in modo improprio per periodi di tempo prolungati. JSR-310 ha progettato l'interfaccia TemporalAmount
per questo scopo.
L
per indicare un long
valore. È così facile interpretare male le lettere minuscole l
per la cifra 1, come appena dimostrato.
Ecco un altro esempio su come formattare la durata. Si noti che questo esempio mostra sia la durata positiva che negativa come durata positiva.
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());
}
}
Questa risposta utilizza solo Duration
metodi e funziona con 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,");
}
Questa è un'opzione di lavoro.
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;
}
Chiama il metodo con
System.out.println(showDuration(LocalTime.of(9, 30, 0, 0)));
Produce qualcosa di simile:
otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463
Che ne dici della seguente funzione, che restituisce + H: MM: SS o + 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 );
}
}
Esiste un approccio abbastanza semplice e (IMO) elegante, almeno per durate inferiori a 24 ore:
DateTimeFormatter.ISO_LOCAL_TIME.format(value.addTo(LocalTime.of(0, 0)))
I formattatori hanno bisogno di un oggetto temporale per formattare, quindi puoi crearne uno aggiungendo la durata a un LocalTime di 00:00 (cioè mezzanotte). Questo ti darà un LocalTime che rappresenta la durata da mezzanotte a quel momento, che è quindi facile da formattare in notazione standard HH: mm: ss. Questo ha il vantaggio di non aver bisogno di una libreria esterna e usa la libreria java.time per fare il calcolo, piuttosto che calcolare manualmente ore, minuti e secondi.
String duration(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();
}
usando questa funzione
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));
}
La mia libreria Time4J offre una soluzione basata su pattern (simile a Apache DurationFormatUtils
, ma più flessibile):
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
Questo codice dimostra le capacità per gestire l'overflow delle ore e la gestione dei segni, vedere anche l'API del formatter di durata basato sul modello .
In scala (ho visto altri tentativi e non sono rimasto colpito):
def formatDuration(duration: Duration): String = {
import duration._ // get access to all the members ;)
f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
}
Sembra orribile sì? Ecco perché usiamo gli IDE per scrivere queste cose in modo che il metodo chiami ($toHoursPart
ecc.) Abbiano un colore diverso.
Si f"..."
tratta di un interpolatore di stringhe in stile printf
/ String.format
(che è ciò che consente il funzionamento $
dell'iniezione di codice) Dato un output di 1 14:06:32.583
, la f
stringa interpolata sarebbe equivalente aString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)
In Scala, sviluppando la soluzione YourBestBet ma semplificando:
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
in scala, nessuna libreria necessaria:
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("")
Non l'ho visto, quindi ho pensato di aggiungerlo:
Date started=new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
task
long duration=new Date().getTime()-started.getTime();
System.out.println(format.format(new Date(duration));
Funziona solo per 24 ore, ma è quello che di solito voglio per la durata.