Estou tentando calcular a diferença entre dois LocalDateTime
.
A saída precisa estar no formato y years m months d days h hours m minutes s seconds
. Aqui está o que eu escrevi:
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
public class Main {
static final int MINUTES_PER_HOUR = 60;
static final int SECONDS_PER_MINUTE = 60;
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
public static void main(String[] args) {
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Period period = getPeriod(fromDateTime, toDateTime);
long time[] = getTime(fromDateTime, toDateTime);
System.out.println(period.getYears() + " years " +
period.getMonths() + " months " +
period.getDays() + " days " +
time[0] + " hours " +
time[1] + " minutes " +
time[2] + " seconds.");
}
private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
return Period.between(dob.toLocalDate(), now.toLocalDate());
}
private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
LocalDateTime today = LocalDateTime.of(now.getYear(),
now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
Duration duration = Duration.between(today, now);
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
return new long[]{hours, minutes, secs};
}
}
A saída que estou recebendo é 29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. Eu verifiquei meu resultado neste site (com valores 12/16/1984 07:45:55
e 09/09/2014 19:46:45
). A captura de tela a seguir mostra a saída:
Tenho certeza de que os campos após o valor do mês estão incorretos no meu código. Qualquer sugestão seria muito útil.
Atualizar
Testei meu resultado em outro site e o resultado obtido é diferente. Aqui está: Calcule a duração entre duas datas (resultado: 29 anos, 8 meses, 24 dias, 12 horas, 0 minutos e 50 segundos).
Atualizar
Como obtive dois resultados diferentes de dois sites diferentes, estou me perguntando se o algoritmo do meu cálculo é legítimo ou não. Se eu usar os seguintes dois LocalDateTime
objetos:
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Então a saída está chegando: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.
A partir deste link deve ser 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds
. Portanto, o algoritmo também precisa lidar com os números negativos.
Observe que a pergunta não é sobre qual site me deu o resultado, preciso conhecer o algoritmo certo e ter os resultados certos.
Period.between()
aplicar alguns arredondamentos?7:45:55
e a hora de término19:46:45
(ou7:46:45
PM). Portanto, a diferença entre essas duas vezes é de 12 horas, 0 minutos e 50 segundos e nunca 23 horas, 34 minutos e 12 segundos. Portanto, seu cálculo atual parece estar correto, pelo menos na parte do tempo.LocalDateTime
não possui fuso horário, pode não haver uma resposta única. Mesmo se você presumir que os fusos horários de início e término são os mesmos, em determinadas zonas como 09-09-2014 será no horário de verão ou no horário de verão e em outros não. Isso pode prejudicar as coisas em uma hora. Portanto, calcular a diferença para o segundo não faz sentido, a menos que isso seja resolvido.LocalDateTime
gera resultados irreais, pois essa classe propositalmente não possui qualquer conceito de fuso horário ou deslocamento do UTC? Para valores realistas, atribua um fuso horário viaZoneId
a ser usadoZonedDateTime
.Respostas:
Infelizmente, não parece haver uma classe de período que abranja também o tempo; portanto, você pode ter que fazer os cálculos por conta própria.
Felizmente, as classes de data e hora têm muitos métodos de utilidade que simplificam isso até certo ponto. Aqui está uma maneira de calcular a diferença, embora não seja necessariamente a mais rápida:
A idéia básica é a seguinte: crie uma data de início temporária e obtenha os anos completos até o fim. Em seguida, ajuste essa data pelo número de anos para que a data de início seja inferior a um ano a partir do final. Repita isso para cada unidade de tempo em ordem decrescente.
Por fim, um aviso de isenção de responsabilidade : não levei em conta fusos horários diferentes (as duas datas devem estar no mesmo fuso horário) e também não testei / verifiquei como o horário de verão ou outras alterações em um calendário (como o fuso horário em Samoa) afeta esse cálculo. Portanto, use com cuidado.
fonte
ChronoUnit
:), mas fazendo o trabalho "manualmente".long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
, retornaria um número maior que 59 por qualquer intervalo de pelo menos 1 hora - e não é isso que o OP deseja. Levando isso em consideração, você não seria capaz de fazer apenas 6 chamadasChronoUnit#between()
e concluir.Encontrei a melhor maneira de fazer isso com o ChronoUnit.
Documentação adicional está aqui: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
fonte
tempDateTime.until( toDateTime, ChronoUnit.YEARS)
porChronoUnit.YEARS.between(fromDate, toDate)
. Mas as linhas adicionais importantes, comotempDateTime.plusYears( years )
etc., estão completamente ausentes na sua resposta, de modo que não ajuda o OP. O algoritmo completo é importante!Aqui está um único exemplo usando Duration e TimeUnit para obter o formato 'hh: mm: ss'.
fonte
String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());
. Os métodos de peça fornecem os números certos para criar uma string. Eu acho que é mais legível.Deveria ser mais simples!
fonte
TL; DR
e, em seguida, usar os métodos
period.getYears()
,period.getMonths()
,period.getDays()
,duration.toHoursPart()
,duration.toMinutesPart()
,duration.toSecondsPart()
.Resposta expandida
Responderei à pergunta original, ou seja, como obter a diferença horária entre dois
LocalDateTimes
em anos, meses, dias, horas e minutos, de modo que a "soma" (veja a nota abaixo) de todos os valores para as diferentes unidades seja igual ao total diferença temporal e de tal modo que o valor em cada unidade é menor do que o lado maior da unidade, ou sejaminutes < 60
,hours < 24
e assim por diante.Dado dois
LocalDateTimes
start
eend
, por exemplopodemos representar o intervalo de tempo absoluto entre os dois com um -
Duration
talvez usandoDuration.between(start, end)
. Mas a maior unidade que podemos extrair de umDuration
é de dias (como uma unidade temporal equivalente a 24h) - veja a nota abaixo para uma explicação. Para usar unidades maiores (meses, anos), podemos representá-loDuration
com um par de (Period
,Duration
), em quePeriod
mede a diferença até uma precisão de dias eDuration
representa o restante:Agora podemos simplesmente usar os métodos definidos
Period
eDuration
extrair as unidades individuais:ou, usando o formato padrão:
Nota sobre anos, meses e dias
Observe que, na
java.time
concepção, "unidades" como "mês" ou "ano" não representam um valor temporal absoluto fixo - elas dependem de data e calendário, como o exemplo a seguir ilustra:fonte
Há algum problema no código Tapas Bose e no código Thomas. Se a diferença de tempo for negativa, o array obtém os valores negativos. Por exemplo, se
retorna 0 anos 0 meses 1 dias -1 horas 0 minutos 0 segundos.
Eu acho que a saída correta é: 0 anos 0 meses 0 dias 23 horas 0 minutos 0 segundos.
Proponho separar as instâncias LocalDateTime nas instâncias LocalDate e LocalTime. Depois disso, podemos obter as instâncias de período e duração do Java 8. A instância Duration é separada no número de dias e no valor do horário do dia (<24h) com a correção subsequente do valor do período. Quando o segundo valor LocalTime está antes do valor firstLocalTime, é necessário reduzir o período por um dia.
Aqui está minha maneira de calcular a diferença LocalDateTime:
O método acima pode ser usado para calcular a diferença de qualquer valor local de data e hora, por exemplo:
É conveniente escrever um teste de unidade para o método acima (ambos são membros da classe PeriodDuration). Aqui está o código:
}
Todos os testes são bem-sucedidos, independentemente do valor do primeiro LocalDateTime ser anterior e para qualquer valor LocalTime.
fonte
java.time.Period
onde os usuários podem aplicar / produzir esses sinais mistos devido a diferentes designs internos).E a versão do @Thomas no Groovy with leva as unidades desejadas em uma lista em vez de codificar os valores. Essa implementação (que pode ser facilmente transportada para Java - tornei explícita a declaração da função) torna a abordagem de Thomas mais reutilizável.
No momento da redação deste texto, o código acima retorna
47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis
. E, para a entrada @Gennady Kolomoets, o código retorna23 Hours
.Quando você fornece uma lista de unidades, ela deve ser classificada por tamanho das unidades (maior primeiro):
fonte
Aqui está uma resposta muito simples para sua pergunta. Funciona.
fonte
Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old
. Eu não acho que isso é o que foi desejado.Joda-Time
Como muitas das respostas exigiam suporte à API 26 e minha API mínima era 23, resolvi-a pelo código abaixo:
fonte
Depois de mais de cinco anos, respondo minha pergunta. Penso que o problema com duração negativa pode ser resolvido com uma simples correção:
Nota: O site https://www.epochconverter.com/date-difference agora calcula corretamente a diferença horária.
Obrigado a todos por sua discussão e sugestões.
fonte
toHours
,toMinutes
etoSeconds
métodos deDuration
. Desde o Java 9 ainda mais a partirtoMinutesPart()
etoSecondsPart()
. E não vejo o sentido de agrupar as horas, os minutos e os segundos em uma matriz apenas para eliminá-los novamente. Geralmente uma boa resposta, no entanto.