Me gustaría tener un método compareTo que ignore la porción de tiempo de un java.util.Date. Supongo que hay varias formas de resolver esto. ¿Cuál es la forma más simple?
Me gustaría tener un método compareTo que ignore la porción de tiempo de un java.util.Date. Supongo que hay varias formas de resolver esto. ¿Cuál es la forma más simple?
Respuestas:
Actualización: aunque Joda Time era una buena recomendación en ese momento, use la java.timebiblioteca de Java 8+ en su lugar siempre que sea posible.
Mi preferencia es usar Joda Time, lo que hace que esto sea increíblemente fácil:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
EDITAR: Como se señaló en los comentarios, si lo usa DateTimeComparator.getDateOnlyInstance()es aún más simple :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
("Use Joda Time" es la base de casi todas las preguntas SO sobre las que se pregunta java.util.Dateo java.util.Calendar. Es una API completamente superior. Si está haciendo algo significativo con las fechas / horas, realmente debería usarlo si es posible).
Si se ve obligado a usar la API integrada, debe crear una instancia Calendarcon la fecha adecuada y con la zona horaria adecuada. Luego, puede establecer cada campo en cada calendario fuera de la hora, minuto, segundo y milisegundo a 0, y comparar los tiempos resultantes. Definitivamente asqueroso en comparación con la solución de Joda :)
La zona horaria parte es importante: java.util.Datees siempre basada en UTC. En la mayoría de los casos en los que he estado interesado en una fecha, esa ha sido una fecha en una zona horaria específica . Eso por sí solo te obligará a usarCalendar o Joda Time (a menos que desee contabilizar la zona horaria usted mismo, lo que no recomiendo).
Referencia rápida para desarrolladores de Android
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Código de muestra (ejemplo)
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
Apache commons-lang es casi omnipresente. ¿Y qué hay de esto?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Si realmente quieres usar java.util.Date, harías algo como esto:
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();
}
}
o, usando un calendario en su lugar (preferido, ya que getYear () y tales están en desuso)
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);
}
}
Preferiría usar la biblioteca Joda insetad de java.util.Datedirectamente, ya que Joda hace una distinción entre fecha y hora (vea las clases YearMonthDay y DateTime ).
Sin embargo, si desea utilizarlo java.util.Date, le sugiero que escriba un método de utilidad; p.ej
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();
}
¿Alguna opinión sobre esta alternativa?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==operador no necesariamente devuelve truecadenas equivalentes (uso equals()). Ciertamente, tampoco puede usar los otros operadores de comparación que mencionó.
sdf.format(date1).compareTo(sdf.format(date2));.
Yo también prefiero Joda Time , pero aquí hay una alternativa:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
EDITAR
Puse la cosa UTC a continuación en caso de que necesite comparar fechas para una zona horaria específica que no sea UTC. Sin embargo, si tienes esa necesidad, te aconsejo que vayas por 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 las viejas clases problemáticas de fecha y hora heredadas como Date&Calendar , ahora suplantadas por las clases java.time.
A java.util.Daterepresenta un momento en la línea de tiempo en UTC. El equivalente en java.time es Instant. Puede convertir utilizando nuevos métodos agregados a la clase heredada.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Quieres comparar por fecha. Una zona horaria es crucial para determinar una fecha. Para cualquier momento dado, la fecha varía en todo el mundo por zona. Por ejemplo, unos minutos después de la medianoche en París, Francia es un nuevo día mientras todavía "ayer" en Montreal Québec .
Especificar un nombre de zona horaria correcta en el formato de continent/region, por ejemplo America/Montreal, Africa/Casablancao Pacific/Auckland. Nunca use el 3-4 abreviatura de letras tales como ESTo IST, ya que son no verdaderas zonas de tiempo, no estandarizados, y ni siquiera único (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Aplica el ZoneIdpara Instantobtener un ZonedDateTime.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
La LocalDateclase representa un valor de solo fecha sin hora del día y sin zona horaria. Podemos extraer a LocalDatede a ZonedDateTime, eliminando efectivamente la parte de la hora del día.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Ahora comparar, utilizando métodos tales como isEqual, isBefore, y isAfter.
Boolean sameDate = localDate1.isEqual( localDate2 );
Vea este código en vivo en IdeOne.com .
instant1: 2017-03-25T04: 13: 10.971Z | instant2: 2017-03-24T22: 13: 10.972Z
zdt1: 2017-03-25T00: 13: 10.971-04: 00 [América / Montreal] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [América / Montreal]
localDate1: 2017-03-25 | localDate2: 2017-03-24
sameDate: false
El marco java.time está integrado en Java 8 y posterior. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, ySimpleDateFormat .
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .
Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
¿Dónde obtener las clases java.time?
El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .
Ya mencioné apache commons-utils :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
le da un objeto Fecha que contiene solo fecha, sin hora, y puede compararlo con Date.compareTo
Me temo que no hay un método para comparar dos fechas que podrían llamarse "fácil" o "simple".
Al comparar dos instancias de tiempo con cualquier tipo de precisión reducida (por ejemplo, solo comparar fechas), siempre debe tener en cuenta cómo la zona horaria afecta la comparación.
Si date1está especificando un evento que ocurrió en la zona horaria +2 y date2está especificando un evento que ocurrió en EST, por ejemplo, debe tener cuidado para comprender adecuadamente las implicaciones de la comparación.
¿Su propósito es determinar si los dos eventos ocurrieron en la misma fecha del calendario en sus propias zonas horarias respectivas? ¿O necesita saber si las dos fechas caen en la misma fecha del calendario en una zona horaria específica (UTC o su TZ local, por ejemplo).
Una vez que descubres qué es lo que realmente estás tratando de comparar, solo es cuestión de obtener el triple de la fecha año-mes en una zona horaria apropiada y hacer la comparación.
El tiempo de Joda puede hacer que la operación de comparación real parezca mucho más limpia, pero la semántica de la comparación sigue siendo algo que debe descubrir usted mismo.
Si está utilizando Java 8, debe usar las java.time.*clases para comparar fechas; es preferible a las distintasjava.util.* clases
p.ej; 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);
Si solo desea comparar dos fechas sin tiempo, el siguiente código podría ayudarlo:
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
}
Aquí hay una solución de este 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.");
es decir, puede ver si la diferencia horaria en milisegundos es menor que la duración de un día.
No sé si es nuevo pensar o de lo contrario, pero te muestro como lo hice
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);
Simplemente marque DAY_OF_YEAR en combinación con la propiedad YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
EDITAR:
Ahora podemos usar el poder de las funciones de extensión de 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();
}
Esto lo ayudará a comparar fechas sin tener en cuenta la hora.
Usando getDateInstance de SimpleDateFormat, podemos comparar solo dos objetos de fecha sin tiempo. Ejecute el siguiente código.
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).
Otro método simple de comparación basado en las respuestas aquí y mi guía de 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());
}
EDITAR : Según @Jonathan Drapeau, el código anterior falla algunos casos (me gustaría ver esos casos, por favor) y sugirió lo siguiente, según entiendo:
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());
}
Tenga en cuenta que la clase Date está en desuso porque no era susceptible de internacionalización. ¡La clase Calendario se usa en su lugar!
getXxxmétodos obsoletos de Date.
Primero, tenga en cuenta que esta operación depende de la zona horaria. Por lo tanto, elija si desea hacerlo en UTC, en la zona horaria de la computadora, en su propia zona horaria favorita o dónde. Si aún no está convencido de que es importante, vea mi ejemplo al final de esta respuesta.
Como su pregunta no es muy clara sobre esto, supongo que tiene una clase con un campo de instancia que representa un punto en el tiempo y la implementación Comparable, y desea que el orden natural de sus objetos sea por fecha, pero no por hora , de ese campo. Por ejemplo:
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.timeClases de Java 8Me he tomado la libertad de cambiar el tipo de su campo de instancia de Datea Instant, la clase correspondiente en Java 8. Prometo volver al tratamiento de Dateabajo. También he agregado una zona horaria constante. Puede establecerlo en ZoneOffset.UTCo ZoneId.of("Europe/Stockholm")lo que considere apropiado (establecerlo en un ZoneOffsettrabajo porque ZoneOffsetes una subclase de ZoneId).
Elegí mostrar la solución usando las clases de Java 8. Pediste la forma más sencilla, ¿verdad? :-) Aquí está el compareTométodo que solicitó:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Si nunca necesita la pieza del tiempo de when, por supuesto, es más fácil para declarar whenuna LocalDatey saltar todas las conversiones. Entonces tampoco tenemos que preocuparnos por la zona horaria.
Ahora suponga que por alguna razón no puede declarar su whencampo Instanto desea mantenerlo a la antigua Date. Si aún puede usar Java 8, simplemente conviértalo Instant, luego haga lo que hizo antes:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
Del mismo modo para o.when .
Si no puede usar Java 8, hay dos opciones:
CalendaroSimpleDateFormat .La respuesta de Adamski le muestra cómo quitar la parte del tiempo de Dateusar la Calendarclase. Le sugiero que utilice getInstance(TimeZone)para obtener la Calendarinstancia para la zona horaria que desee. Como alternativa, puede usar la idea de la segunda mitad de la respuesta de Jorn .
Usar SimpleDateFormates realmente una forma indirecta de usar Calendarya que a SimpleDateFormatcontiene un Calendarobjeto. Sin embargo, puede encontrarlo menos problemático que usarlo Calendardirectamente:
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));
}
Esto fue inspirado por la respuesta de Rob .
¿Por qué tenemos que elegir una zona horaria específica? Digamos que queremos comparar dos veces que en UTC son 24 de marzo 0:00 (medianoche) y 12:00 (mediodía). Si hace eso en CET (digamos, Europa / París), son las 1 am y la 1 pm el 24 de marzo, es decir, la misma fecha. En Nueva York (hora de verano del este), son las 20:00 el 23 de marzo y las 8:00 el 24 de marzo, es decir, no es la misma fecha. Por lo tanto, hace una diferencia la zona horaria que elija. Si solo confía en el valor predeterminado de la computadora, puede encontrarse con sorpresas cuando alguien intenta ejecutar su código en una computadora en otro lugar de este mundo globalizado.
Enlace a ThreeTen Backport, el backport de las clases de fecha y hora de Java 8 a Java 6 y 7: http://www.threeten.org/threetenbp/ .
Mi propuesta:
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 es la forma más sencilla de resolver este problema.
Resolví esto comparando por marca de tiempo:
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 Joda Time porque en Android usa un gran espacio. El tamaño importa. ;)
Otra solución que usa Java 8 e Instant, es usar el método truncatedTo
Devuelve una copia de este Instant truncado a la unidad especificada.
Ejemplo:
@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)
}
Esto es lo que funcionó para mí:
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 }