Respostas:
Se você estiver usando uma versão do Java anterior a 8 ... poderá usar o Joda Time e PeriodFormatter
. Se você realmente tem uma duração (isto é, uma quantidade de tempo decorrido, sem referência a um sistema de calendário), provavelmente deve estar usando Duration
a maior parte do tempo - pode ligar toPeriod
(especificando o PeriodType
que deseja refletir se 25 horas se tornar 1 dia e 1 hora ou não, etc) para obter um Period
formato que você possa formatar.
Se você estiver usando o Java 8 ou posterior: normalmente sugiro usar java.time.Duration
para representar a duração. Em seguida, você pode ligar getSeconds()
ou algo semelhante para obter um número inteiro para a formatação de sequência padrão, conforme a resposta de bobince, se necessário - embora você deva ter cuidado com a situação em que a duração é negativa, pois provavelmente deseja um único sinal negativo na sequência de saída . Então, algo como:
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;
}
Formatar dessa maneira é razoavelmente simples, embora irritantemente manual. Para analisar , torna-se um assunto mais difícil em geral ... Você ainda pode usar o Joda Time, mesmo com o Java 8, se quiser, é claro.
Duration
métodos s: duration.toHours()
, duration.toMinutesPart()
e duration.toSecondsPart()
que estão disponíveis desde Java 9. E para obter o valor de um absoluto pode usar duration.abs()
.
Se você não deseja arrastar as bibliotecas, é simples o suficiente para fazer você mesmo usando um Formatador ou um atalho relacionado, por exemplo. dado número inteiro de segundos s:
String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
s/86400, (s%86400)/3600...
por dias, se necessário ...
s > 8640
0 (um dia): "\u221E"
- infinito
Eu uso o DurationFormatUtils do Apache common assim:
DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
Isso é mais fácil desde o Java 9. A Duration
ainda não é formalizável, mas são adicionados métodos para obter horas, minutos e segundos, o que torna a tarefa um pouco mais direta:
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);
A saída desse 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"));
antes de usar a format
função.
Isso pode ser meio hacky, mas é uma boa solução se alguém se empenhar em fazer isso usando o 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)
), obteria um UnsupportedTemporalTypeException
). Eu simplesmente removi o código, verificando se a diferença é == 01
. Achei que 01
era algum tipo de valor mascarado que ainda não entendi, você pode explicar?
isSupported
método está retornando as informações se houver um campo válido para mostrar na sequência legível por humanos. De qualquer forma, o "mascaramento" não é realmente uma máscara. O código return value!=0l
não mostra return value!=01
. Por favor, use copiar e colar da próxima vez.
TemporalAccessor
não deve ser mal utilizada por durações. O JSR-310 projetou a interface TemporalAmount
para esse fim.
L
para significar um long
valor. É tão fácil interpretar em minúsculas o l
dígito 1, como demonstrado.
Aqui está mais um exemplo de como formatar a duração. Observe que esta amostra mostra duração positiva e negativa como duração 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());
}
}
Esta resposta usa apenas Duration
métodos e funciona com 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,");
}
Que tal a seguinte função, que retorna + H: MM: SS ou + 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 );
}
}
Há uma abordagem bastante simples e elegante (IMO), pelo menos por períodos inferiores a 24 horas:
DateTimeFormatter.ISO_LOCAL_TIME.format(value.addTo(LocalTime.of(0, 0)))
Os formatadores precisam de um objeto temporal para formatar, para que você possa criar um adicionando a duração a um LocalTime de 00:00 (ou seja, meia-noite). Isso fornecerá um LocalTime representando a duração da meia-noite até a hora, que é fácil de formatar na notação HH: mm: ss padrão. Isso tem a vantagem de não precisar de uma biblioteca externa e usa a biblioteca java.time para fazer o cálculo, em vez de calcular manualmente as horas, minutos e segundos.
Esta é uma opção de trabalho.
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;
}
Chame o método com
System.out.println(showDuration(LocalTime.of(9, 30, 0, 0)));
Produz algo como:
otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463
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();
}
Minha biblioteca Time4J oferece uma solução baseada em padrões (semelhante a Apache DurationFormatUtils
, mas mais flexível):
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
Esse código demonstra os recursos para lidar com excesso de horas e manipulação de sinais; consulte também a API do formatador de duração com base no padrão .
Em scala (vi outras tentativas e não fiquei impressionado):
def formatDuration(duration: Duration): String = {
import duration._ // get access to all the members ;)
f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
}
Parece horrível, sim? Bem, é por isso que usamos IDEs para escrever essas coisas, para que as chamadas de método ( $toHoursPart
etc) sejam de cores diferentes.
O f"..."
é um printf
/String.format
interpolador de cadeia de caracteres style (que é o que permite que a $
injeção de código funcione) Dada uma saída de 1 14:06:32.583
, a f
cadeia de caracteres interpolada seria equivalente aString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)
usando esta função
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));
}
Na Scala, desenvolvendo a solução da YourBestBet, mas simplificado:
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
no scala, nenhuma biblioteca é necessária:
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("")
Não vi este, então pensei em adicioná-lo:
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));
Só funciona por 24 horas, mas é o que eu normalmente quero por duração.