Vorrei avere un metodo compareTo che ignori la parte temporale di un java.util.Date. Immagino che ci siano molti modi per risolverlo. Qual è il modo più semplice?
Vorrei avere un metodo compareTo che ignori la parte temporale di un java.util.Date. Immagino che ci siano molti modi per risolverlo. Qual è il modo più semplice?
Risposte:
Aggiornamento: mentre Joda Time era una buona raccomandazione al momento, utilizzare la java.timelibreria da Java 8+ invece, ove possibile.
La mia preferenza è usare Joda Time, il che rende questo incredibilmente facile:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
EDIT: come notato nei commenti, se lo usi DateTimeComparator.getDateOnlyInstance()è ancora più semplice :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
("Usa Joda Time" è la base di quasi tutte le domande SO che pongono java.util.Dateo java.util.Calendar. È un'API di qualità superiore. Se stai facendo qualcosa di significativo con date / orari, dovresti davvero usarlo se possibile.)
Se sei assolutamente costretto a utilizzare l'API integrata, devi creare un'istanza Calendarcon la data appropriata e utilizzando il fuso orario appropriato. È quindi possibile impostare ciascun campo in ciascun calendario su ore, minuti, secondi e millisecondi su 0 e confrontare i tempi risultanti. Sicuramente icky rispetto alla soluzione Joda però :)
La parte del fuso orario è importante: java.util.Datesi basa sempre su UTC. Nella maggior parte dei casi in cui sono stato interessato a una data, quella è stata una data in un fuso orario specifico . Che da solo ti costringerà a utilizzare Calendaro Joda Time (a meno che tu non voglia tenere conto del fuso orario, che non consiglio.)
Riferimento rapido per gli sviluppatori Android
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Codice di esempio (esempio)
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 è quasi onnipresente. E allora?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Se vuoi davvero usare java.util.Date, dovresti fare qualcosa del genere:
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();
}
}
oppure, usando invece un Calendario (preferito, poiché getYear () e simili sono obsoleti)
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);
}
}
La mia preferenza sarebbe quella di utilizzare la libreria Jodajava.util.Date direttamente, poiché Joda fa una distinzione tra data e ora (vedi le classi YearMonthDay e DateTime ).
Tuttavia, se si desidera utilizzare java.util.Date, suggerirei di scrivere un metodo di utilità; per esempio
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();
}
Qualche opinione su questa alternativa?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==operatore non ritorna necessariamente trueper stringhe equivalenti (uso equals()). Certamente non puoi nemmeno usare gli altri operatori di confronto che hai citato.
sdf.format(date1).compareTo(sdf.format(date2));.
Anch'io preferisco Joda Time , ma ecco un'alternativa:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
MODIFICARE
Ho messo la cosa UTC qui sotto nel caso in cui sia necessario confrontare le date per un fuso orario specifico diverso da UTC. Se hai un tale bisogno, però, ti consiglio davvero di scegliere 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()
)
Evita le fastidiose vecchie classi di data e ora come Date& Calendar, ora soppiantate dalle classi java.time.
A java.util.Daterappresenta un momento nella sequenza temporale in UTC. L'equivalente in java.time è Instant. È possibile convertire utilizzando nuovi metodi aggiunti alla classe legacy.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Vuoi confrontare per data. Un fuso orario è fondamentale per determinare una data. Per ogni dato momento, la data varia intorno al globo per zona. Ad esempio, pochi minuti dopo la mezzanotte a Parigi, la Francia è un nuovo giorno mentre è ancora “ieri” a Montréal Québec .
Specificare un nome proprio fuso orario nel formato continent/region, come ad esempio America/Montreal, Africa/CasablancaoPacific/Auckland . Non usare mai il 3-4 lettera sigla come ad esempio ESTo ISTcome sono non fusi orari veri e propri, non standardizzati, e nemmeno unico (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Applicare ZoneIda Instantper ottenere a ZonedDateTime.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
Il LocalDate classe rappresenta un valore solo per la data senza ora del giorno e senza fuso orario. Siamo in grado di estrarre un LocalDateda un ZonedDateTime, eliminando efficacemente la porzione dell'ora del giorno.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Ora confronta, usando metodi come isEqual , isBeforee isAfter.
Boolean sameDate = localDate1.isEqual( localDate2 );
Vedi questo codice eseguito dal vivo su 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 [America / Montreal] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [America / Montreal]
localDate1: 25-03-2017 | localDate2: 24-03-2017
sameDate: false
Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, eSimpleDateFormat .
Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione a java.time classi .
Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .
Dove ottenere le classi java.time?
Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Si possono trovare alcune classi utili, come per esempio Interval, YearWeek, YearQuartere altro .
Già menzionato apache commons-utils :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
ti dà un oggetto Date contenente solo la data, senza ora, e puoi confrontarlo con Date.compareTo
Temo che non esista un metodo per confrontare due date che potrebbero essere definite "facili" o "semplici".
Quando si confrontano due istanze temporali con qualsiasi tipo di precisione ridotta (ad esempio solo il confronto delle date), è necessario tenere sempre conto di come il fuso orario influisce sul confronto.
Se date1si specifica un evento che si è verificato nel fuso orario +2 e date2si specifica un evento che si è verificato in EST, ad esempio, è necessario fare attenzione a comprendere correttamente le implicazioni del confronto.
Il tuo scopo è capire se i due eventi si sono verificati nella stessa data di calendario nei rispettivi fusi orari? Oppure devi sapere se le due date cadono nella stessa data di calendario in un fuso orario specifico (UTC o la TZ locale, per esempio).
Una volta capito che cosa stai effettivamente cercando di confrontare, è solo una questione di ottenere la tripla anno-mese-data in un fuso orario appropriato ed eseguire il confronto.
Il tempo di Joda potrebbe rendere l'operazione di confronto reale molto più pulita, ma la semantica del confronto è ancora qualcosa che devi capire da solo.
Se stai usando Java 8, dovresti usare le java.time.*classi per confrontare le date - è preferito alle variejava.util.* classi
per esempio; 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 vuoi solo confrontare solo due date senza tempo, il seguente codice potrebbe aiutarti:
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
}
Ecco una soluzione da questo 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.");
cioè puoi vedere se la differenza di tempo in millisecondi è inferiore alla lunghezza di un giorno.
Non so che è nuovo pensare altrimenti, ma ti faccio vedere come ho fatto
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);
Controlla semplicemente DAY_OF_YEAR in combinazione con la proprietà YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
MODIFICARE:
Ora possiamo usare la potenza delle funzioni di estensione di 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();
}
Questo ti aiuterà a confrontare le date senza considerare l'ora.
Usando getDateInstance di SimpleDateFormat, possiamo confrontare solo due oggetti data senza ora. Eseguire il codice seguente.
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));
}
Utilizzando 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).
Un altro metodo di confronto semplice basato sulle risposte qui e sulla mia guida del mentore
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 : Secondo @Jonathan Drapeau, il codice sopra fallisce alcuni casi (mi piacerebbe vedere quei casi, per favore) e ha suggerito quanto segue a quanto ho capito:
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());
}
Si noti che la classe Date è obsoleta perché non era suscettibile di internazionalizzazione. Al suo posto viene utilizzata la classe Calendar!
getXxxmetodi obsoleti di Date.
Innanzitutto, tenere presente che questa operazione dipende dal fuso orario. Quindi scegli se vuoi farlo in UTC, nel fuso orario del computer, nel tuo fuso orario preferito o dove. Se non sei ancora convinto che sia importante, vedi il mio esempio in fondo a questa risposta.
Dal momento che la tua domanda non è abbastanza chiara a riguardo, presumo che tu abbia una classe con un campo di istanza che rappresenta un punto nel tempo e un'implementazione Comparablee desideri che l'ordine naturale dei tuoi oggetti sia entro la data, ma non l'ora , di quel campo. Per esempio:
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.timeClassi Java 8Ho preso la libertà di cambiare il tipo del campo della tua istanza da Datea Instant, la classe corrispondente in Java 8. Prometto di tornare al trattamento di Dateseguito. Ho anche aggiunto una costante di fuso orario. Puoi impostarlo su ZoneOffset.UTCo su ZoneId.of("Europe/Stockholm")ciò che ritieni appropriato (impostandolo su un'opera ZoneOffsetperché ZoneOffsetè una sottoclasse di ZoneId).
Ho scelto di mostrare la soluzione usando le classi Java 8. Hai chiesto il modo più semplice, giusto? :-) Ecco il compareTometodo che hai richiesto:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Se non hai mai bisogno del tempo when, è ovviamente più facile dichiarare whena LocalDatee saltare tutte le conversioni. Quindi non dobbiamo più preoccuparci nemmeno del fuso orario.
Supponiamo ora che per qualche motivo non puoi dichiarare il tuo whencampo Instanto vuoi mantenerlo vecchio stile Date. Se puoi ancora usare Java 8, convertilo in Instant, quindi fai come prima:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
Allo stesso modo per o.when.
Se non puoi usare java 8, ci sono due opzioni:
Calendaro SimpleDateFormat.La risposta di Adamski ti mostra come eliminare il tempo parziale Dateusando una Calendarclasse. Ti suggerisco di utilizzare getInstance(TimeZone)per ottenere l' Calendaristanza per il fuso orario desiderato. In alternativa puoi usare l'idea della seconda metà della risposta di Jorn .
L'uso SimpleDateFormatè davvero un modo indiretto di usare Calendarpoiché a SimpleDateFormatcontiene un Calendaroggetto. Tuttavia, potresti trovarlo meno problematico rispetto all'utilizzo Calendardiretto:
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));
}
Questo è stato ispirato dalla risposta di Rob .
Perché dobbiamo scegliere un fuso orario specifico? Diciamo che vogliamo confrontare due volte che in UTC sono il 24 marzo 0:00 (mezzanotte) e 12:00 (mezzogiorno). Se lo fai in CET (diciamo, Europa / Parigi), sono le 1:00 e le 13:00 del 24 marzo, ovvero la stessa data. A New York (Eastern Daylight Time), sono le 20:00 del 23 marzo e le 8:00 del 24 marzo, ovvero non la stessa data. Quindi fa la differenza quale fuso orario scegli. Se fai affidamento solo sul valore predefinito del computer, potresti essere sorpreso quando qualcuno tenta di eseguire il codice su un computer in un altro posto in questo mondo globalizzato.
Link a ThreeTen Backport, il backport delle classi di data e ora di Java 8 a Java 6 e 7: http://www.threeten.org/threetenbp/ .
La mia proposta:
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
questo è il modo più semplice per risolvere questo problema.
Ho risolto questo confronto confrontando per timestamp:
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 di usare Joda Time perché su Android utilizza uno spazio enorme. Le misure contano. ;)
Un'altra soluzione che utilizza Java 8 e Instant, sta usando il metodo truncatedTo
Restituisce una copia di questo Instant troncato all'unità specificata.
Esempio:
@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)
}
Questo è ciò che ha funzionato per me:
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 }