Eu gostaria de ter um método compareTo que ignore a parte do tempo de um java.util.Date. Eu acho que existem várias maneiras de resolver isso. Qual é a maneira mais simples?
Eu gostaria de ter um método compareTo que ignore a parte do tempo de um java.util.Date. Eu acho que existem várias maneiras de resolver isso. Qual é a maneira mais simples?
Respostas:
Atualização: enquanto o Joda Time era uma boa recomendação na época, use a java.timebiblioteca do Java 8+, sempre que possível.
Minha preferência é usar o Joda Time, o que torna incrivelmente fácil:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
EDIT: Como observado nos comentários, se você usar DateTimeComparator.getDateOnlyInstance(), é ainda mais simples :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
("Use o tempo de Joda" é a base de quase todas as perguntas de SO que perguntam sobre java.util.Dateou java.util.Calendar. É uma API completamente superior. Se você está fazendo algo significativo com datas / horas, deve realmente usá-lo, se puder.)
Se você for absolutamente forçado a usar a API interna, crie uma instância Calendarcom a data apropriada e usando o fuso horário apropriado. Em seguida, você pode definir cada campo em cada calendário de horas, minutos, segundos e milissegundos para 0 e comparar os tempos resultantes. Definitivamente nojento em comparação com a solução Joda :)
A parte do fuso horário é importante: java.util.Dateé sempre baseada no UTC. Na maioria dos casos em que tenho interesse em uma data, essa é uma data em um fuso horário específico . Que por si só forçará você a usarCalendar Joda Time (a menos que você queira contabilizar o fuso horário, o que eu não recomendo.)
Referência rápida para desenvolvedores do Android
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Código de exemplo (exemplo)
DateTimeComparator dateTimeComparator = DateTimeComparator.getDateOnlyInstance();
Date myDateOne = ...;
Date myDateTwo = ...;
int retVal = dateTimeComparator.compare(myDateOne, myDateTwo);
if(retVal == 0)
//both dates are equal
else if(retVal < 0)
//myDateOne is before myDateTwo
else if(retVal > 0)
//myDateOne is after myDateTwo
O Apache commons-lang é quase onipresente. E daí?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Se você realmente deseja usar o java.util.Date, faça algo assim:
public class TimeIgnoringComparator implements Comparator<Date> {
public int compare(Date d1, Date d2) {
if (d1.getYear() != d2.getYear())
return d1.getYear() - d2.getYear();
if (d1.getMonth() != d2.getMonth())
return d1.getMonth() - d2.getMonth();
return d1.getDate() - d2.getDate();
}
}
ou, em vez disso, usando um calendário (preferível, pois getYear () e outros itens obsoletos)
public class TimeIgnoringComparator implements Comparator<Calendar> {
public int compare(Calendar c1, Calendar c2) {
if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR))
return c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR);
if (c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH))
return c1.get(Calendar.MONTH) - c2.get(Calendar.MONTH);
return c1.get(Calendar.DAY_OF_MONTH) - c2.get(Calendar.DAY_OF_MONTH);
}
}
Minha preferência seria usar a biblioteca Jodajava.util.Date diretamente, já que Joda faz uma distinção entre data e hora (consulte YearMonthDay e DateTime classes ).
No entanto, se você deseja usar java.util.Date, sugiro escrever um método utilitário; por exemplo
public static Date setTimeToMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime( date );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Alguma opinião sobre essa alternativa?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==operador não retorna necessariamente truepara Strings equivalentes (uso equals()). Você certamente também não pode usar os outros operadores de comparação mencionados.
sdf.format(date1).compareTo(sdf.format(date2));.
Eu também prefiro o Joda Time , mas aqui está uma alternativa:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
EDITAR
Coloquei o UTC abaixo, caso você precise comparar datas para um fuso horário específico que não seja o UTC. Se você tem essa necessidade, no entanto, eu realmente recomendo ir para Joda.
long oneDay = 24 * 60 * 60 * 1000
long hoursFromUTC = -4 * 60 * 60 * 1000 // EST with Daylight Time Savings
long d1 = (first.getTime() + hoursFromUTC) / oneDay
long d2 = (second.getTime() + hoursFromUTC) / oneDay
d1 == d2
Math.abs(second.getTime() - first.getTime()) < 1000L*60*60*24
< oneDay.
myJavaUtilDate1.toInstant()
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate()
.isEqual (
myJavaUtilDate2.toInstant()
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate()
)
Evite as classes de data e hora antigas e problemáticas, como Date& Calendar, agora suplantadas pelas classes java.time.
A java.util.Daterepresenta um momento na linha do tempo no UTC. O equivalente em java.time é Instant. Você pode converter usando novos métodos adicionados à classe herdada.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Você deseja comparar por data. Um fuso horário é crucial para determinar uma data. Por um determinado momento, a data varia em todo o mundo por zona. Por exemplo, alguns minutos depois da meia-noite em Paris, a França é um novo dia, enquanto ainda é "ontem" em Montreal, no Quebec .
Especifique um nome próprio fuso horário no formato continent/region, como America/Montreal, Africa/Casablanca, ou Pacific/Auckland. Nunca utilize o 3-4 letras, como ESTou ISTcomo eles são não zonas verdadeiros tempo, não padronizados, e nem mesmo única (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Aplique o ZoneIdao Instantpara obter um ZonedDateTime.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
A LocalDateclasse representa um valor somente de data, sem hora do dia e sem fuso horário. Podemos extrair a LocalDatede a ZonedDateTime, eliminando efetivamente a parte da hora do dia.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Agora compare, usando métodos como isEqual, isBefore, e isAfter.
Boolean sameDate = localDate1.isEqual( localDate2 );
Veja este código executado ao vivo em IdeOne.com .
instant1: 2017-03-25T04: 13: 10.971Z | Por que você está reportando essa página?
zdt1: 2017-03-25T00: 13: 10.971-04: 00 [America / Montreal] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [America / Montreal]
localDate1: 25-03-2017 | localDate2: 2017-03-24
sameDate: false
A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, eSimpleDateFormat .
O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para o java.time classes .
Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .
Onde obter as classes java.time?
O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .
O apache commons-utils já mencionado :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
fornece a você o objeto Date que contém apenas data, sem hora, e você pode compará-lo com Date.compareTo
Receio que não exista um método de comparar duas datas que possam ser chamadas de "fácil" ou "simples".
Ao comparar duas instâncias de tempo com qualquer tipo de precisão reduzida (por exemplo, apenas comparar datas), você sempre deve levar em consideração como o fuso horário afeta a comparação.
If date1está especificando um evento que ocorreu no fuso horário +2 edate2 especificando um evento que ocorreu na EST, por exemplo, você deve ter o cuidado de entender corretamente as implicações da comparação.
Seu objetivo é descobrir se os dois eventos ocorreram na mesma data do calendário em seus respectivos fusos horários? Ou Você precisa saber se as duas datas se enquadram na mesma data do calendário em um fuso horário específico (UTC ou sua TZ local, por exemplo).
Depois de descobrir o que realmente está tentando comparar, é apenas uma questão de triplicar a data do ano-mês em um fuso horário apropriado e fazer a comparação.
O tempo de Joda pode fazer com que a operação de comparação real pareça muito mais limpa, mas a semântica da comparação ainda é algo que você precisa descobrir por si mesmo.
Se você estiver usando Java 8, use as java.time.*classes para comparar datas - é preferível às váriasjava.util.* classes
por exemplo; https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
LocalDate date1 = LocalDate.of(2016, 2, 14);
LocalDate date2 = LocalDate.of(2015, 5, 23);
date1.isAfter(date2);
Se você deseja comparar apenas duas datas sem tempo, o código a seguir pode ajudá-lo:
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
Date dLastUpdateDate = dateFormat.parse(20111116);
Date dCurrentDate = dateFormat.parse(dateFormat.format(new Date()));
if (dCurrentDate.after(dLastUpdateDate))
{
add your logic
}
Aqui está uma solução deste blog: http://brigitzblog.blogspot.com/2011/10/java-compare-dates.html
long milliseconds1 = calendar1.getTimeInMillis();
long milliseconds2 = calendar2.getTimeInMillis();
long diff = milliseconds2 - milliseconds1;
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("Time in days: " + diffDays + " days.");
ou seja, você pode ver se a diferença horária em milissegundos é menor que a duração de um dia.
Eu não sei, é novo pensamento ou então, mas eu mostro a você como eu fiz
SimpleDateFormat dtf = new SimpleDateFormat("dd/MM/yyyy");
Date td_date = new Date();
String first_date = dtf.format(td_date); //First seted in String
String second_date = "30/11/2020"; //Second date you can set hear in String
String result = (first_date.equals(second_date)) ? "Yes, Its Equals":"No, It is not Equals";
System.out.println(result);
Basta marcar DAY_OF_YEAR em combinação com a propriedade YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
EDITAR:
Agora podemos usar o poder das funções de extensão Kotlin
fun Calendar.isSameDay(second: Calendar): Boolean {
return this[Calendar.YEAR] == second[Calendar.YEAR] && this[Calendar.DAY_OF_YEAR] == second[Calendar.DAY_OF_YEAR]
}
fun Calendar.compareDatesOnly(other: Calendar): Int {
return when {
isSameDay(other) -> 0
before(other) -> -1
else -> 1
}
}
public Date saveDateWithoutTime(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime( date );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Isso o ajudará a comparar datas sem considerar a hora.
Usando o getDateInstance de SimpleDateFormat, podemos comparar apenas dois objetos de data sem hora. Execute o código abaixo.
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
DateFormat dfg = SimpleDateFormat.getDateInstance(DateFormat.DATE_FIELD);
String dateDtr1 = dfg.format(date1);
String dateDtr2 = dfg.format(date2);
System.out.println(dateDtr1+" : "+dateDtr2);
System.out.println(dateDtr1.equals(dateDtr2));
}
Usando http://mvnrepository.com/artifact/commons-lang/commons-lang
Date date1 = new Date();
Date date2 = new Date();
if (DateUtils.truncatedCompareTo(date1, date2, Calendar.DAY_OF_MONTH) == 0)
// TRUE
else
// FALSE
DateUtils.isSameDay(date1, date2).
Outro método simples de comparação com base nas respostas aqui e na orientação do meu mentor
public static int compare(Date d1, Date d2) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(d1);
c1.set(Calendar.MILLISECOND, 0);
c1.set(Calendar.SECOND, 0);
c1.set(Calendar.MINUTE, 0);
c1.set(Calendar.HOUR_OF_DAY, 0);
c2.setTime(d2);
c2.set(Calendar.MILLISECOND, 0);
c2.set(Calendar.SECOND, 0);
c2.set(Calendar.MINUTE, 0);
c2.set(Calendar.HOUR_OF_DAY, 0);
return c1.getTime().compareTo(c2.getTime());
}
EDIT : De acordo com @ Jonathan Drapeau, o código acima falhou em alguns casos (eu gostaria de ver esses casos, por favor) e ele sugeriu o seguinte como eu entendo:
public static int compare2(Date d1, Date d2) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.clear();
c2.clear();
c1.set(Calendar.YEAR, d1.getYear());
c1.set(Calendar.MONTH, d1.getMonth());
c1.set(Calendar.DAY_OF_MONTH, d1.getDay());
c2.set(Calendar.YEAR, d2.getYear());
c2.set(Calendar.MONTH, d2.getMonth());
c2.set(Calendar.DAY_OF_MONTH, d2.getDay());
return c1.getTime().compareTo(c2.getTime());
}
Observe que a classe Date está obsoleta porque não foi passível de internacionalização. A classe Calendar é usada em seu lugar!
getXxxmétodos obsoletos de Date.
Primeiro, lembre-se de que esta operação depende do fuso horário. Portanto, escolha se deseja fazê-lo no UTC, no fuso horário do computador, no seu fuso horário favorito ou onde. Se você ainda não está convencido de que isso é importante, veja meu exemplo na parte inferior desta resposta.
Como sua pergunta não é clara sobre isso, suponho que você tenha uma classe com um campo de instância representando um ponto no tempo e a implementação Comparable, e deseje que a ordem natural de seus objetos seja a data, mas não a hora , desse campo. Por exemplo:
public class ArnesClass implements Comparable<ArnesClass> {
private static final ZoneId arnesTimeZone = ZoneId.systemDefault();
private Instant when;
@Override
public int compareTo(ArnesClass o) {
// question is what to put here
}
}
java.timeClasses Java 8Tomei a liberdade de alterar o tipo do seu campo de instância de Datepara Instant, a classe correspondente no Java 8. Prometo retornar ao tratamento Dateabaixo. Também adicionei uma constante de fuso horário. Você pode configurá-lo como ZoneOffset.UTCou ZoneId.of("Europe/Stockholm")o que achar apropriado (configurá-lo como um ZoneOffsettrabalho porque ZoneOffseté uma subclasse de ZoneId).
Eu escolhi mostrar a solução usando as classes Java 8. Você pediu a maneira mais simples, certo? :-) Aqui está o compareTométodo que você pediu:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Se você nunca precisa a parte da hora when, é claro que é mais fácil para declarar whenum LocalDatee ignorar todas as conversões. Então não precisamos mais nos preocupar com o fuso horário.
Agora, suponha que, por algum motivo, você não possa declarar seu whencampo Instantou deseja mantê-lo antiquado Date. Se você ainda pode usar o Java 8, basta convertê-lo em Instante faça como antes:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
Da mesma forma para o.when.
Se você não pode usar o java 8, há duas opções:
Calendarou SimpleDateFormat.A resposta de Adamski mostra como tirar a parte do tempo Dateda Calendaraula. Eu sugiro que você use getInstance(TimeZone)para obter a Calendarinstância para o fuso horário desejado. Como alternativa, você pode usar a ideia da segunda metade da resposta de Jorn .
Usar SimpleDateFormaté realmente uma maneira indireta de usar, Calendarpois a SimpleDateFormatcontém um Calendarobjeto. No entanto, você pode achar menos problemático do que usar Calendardiretamente:
private static final TimeZone arnesTimeZone = TimeZone.getTimeZone("Europe/Stockholm");
private static final DateFormat formatter = new SimpleDateFormat("yyyyMMdd");
static {
formatter.setTimeZone(arnesTimeZone);
}
private Date when;
@Override
public int compareTo(ArnesClass o) {
return formatter.format(when).compareTo(formatter.format(o.when));
}
Isso foi inspirado na resposta de Rob .
Por que precisamos escolher um fuso horário específico? Digamos que queremos comparar duas vezes que no UTC são 24 de março às 0:00 (meia-noite) e 12:00 (meio-dia). Se você fizer isso na CET (digamos, Europa / Paris), são 1 e 13 do dia 24 de março, ou seja, a mesma data. Em Nova York (horário de verão do leste), são 20:00 em 23 de março e 8:00 em 24 de março, ou seja, não é a mesma data. Portanto, faz diferença o fuso horário escolhido. Se você apenas confiar no padrão do computador, poderá ter surpresas quando alguém tentar executar seu código em um computador em outro lugar neste mundo globalizado.
Link para o ThreeTen Backport, o backport das classes de data e hora do Java 8 para Java 6 e 7: http://www.threeten.org/threetenbp/ .
Minha proposição:
Calendar cal = Calendar.getInstance();
cal.set(1999,10,01); // nov 1st, 1999
cal.set(Calendar.AM_PM,Calendar.AM);
cal.set(Calendar.HOUR,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
// date column in the Thought table is of type sql date
Thought thought = thoughtDao.getThought(date, language);
Assert.assertEquals(cal.getTime(), thought.getDate());
public static Date getZeroTimeDate(Date fecha) {
Date res = fecha;
Calendar calendar = Calendar.getInstance();
calendar.setTime( fecha );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
res = calendar.getTime();
return res;
}
Date currentDate = getZeroTimeDate(new Date());// get current date
esta é a maneira mais simples de resolver esse problema.
Resolvi isso comparando com carimbo de data e hora:
Calendar last = Calendar.getInstance();
last.setTimeInMillis(firstTimeInMillis);
Calendar current = Calendar.getInstance();
if (last.get(Calendar.DAY_OF_MONTH) != current.get(Calendar.DAY_OF_MONTH)) {
//not the same day
}
Evito usar o Joda Time porque no Android ocupa um espaço enorme. Tamanho importa. ;)
Outra solução usando Java 8 e Instant, está usando o método truncatedTo
Retorna uma cópia deste instante truncada para a unidade especificada.
Exemplo:
@Test
public void dateTruncate() throws InterruptedException {
Instant now = Instant.now();
Thread.sleep(1000*5);
Instant later = Instant.now();
assertThat(now, not(equalTo(later)));
assertThat(now.truncatedTo(ChronoUnit.DAYS), equalTo(later.truncatedTo(ChronoUnit.DAYS)));
}
// Create one day 00:00:00 calendar
int oneDayTimeStamp = 1523017440;
Calendar oneDayCal = Calendar.getInstance();
oneDayCal.setTimeInMillis(oneDayTimeStamp * 1000L);
oneDayCal.set(Calendar.HOUR_OF_DAY, 0);
oneDayCal.set(Calendar.MINUTE, 0);
oneDayCal.set(Calendar.SECOND, 0);
oneDayCal.set(Calendar.MILLISECOND, 0);
// Create current day 00:00:00 calendar
Calendar currentCal = Calendar.getInstance();
currentCal.set(Calendar.HOUR_OF_DAY, 0);
currentCal.set(Calendar.MINUTE, 0);
currentCal.set(Calendar.SECOND, 0);
currentCal.set(Calendar.MILLISECOND, 0);
if (oneDayCal.compareTo(currentCal) == 0) {
// Same day (excluding time)
}
Isto é o que funcionou para mim:
var Date1 = new Date(dateObject1.toDateString()); //this sets time to 00:00:00
var Date2 = new Date(dateObject2.toDateString());
//do a normal compare
if(Date1 > Date2){ //do something }