Je voudrais avoir une méthode compareTo qui ignore la partie temporelle d'un java.util.Date. Je suppose qu'il y a plusieurs façons de résoudre ce problème. Quelle est la manière la plus simple?
Je voudrais avoir une méthode compareTo qui ignore la partie temporelle d'un java.util.Date. Je suppose qu'il y a plusieurs façons de résoudre ce problème. Quelle est la manière la plus simple?
Réponses:
Mise à jour: alors que Joda Time était une bonne recommandation à l'époque, utilisez java.time
plutôt la bibliothèque de Java 8+ lorsque cela est possible.
Ma préférence est d'utiliser Joda Time, ce qui rend cela incroyablement facile:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
EDIT: Comme indiqué dans les commentaires, si vous utilisez DateTimeComparator.getDateOnlyInstance()
c'est encore plus simple :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
("Utiliser Joda Time" est la base de presque toutes les questions SO qui posent des questions sur java.util.Date
ou java.util.Calendar
. C'est une API complètement supérieure. Si vous faites quelque chose d' important avec les dates / heures, vous devriez vraiment l'utiliser si vous le pouvez.)
Si vous êtes absolument obligé d'utiliser l'API intégrée, vous devez créer une instance de Calendar
avec la date appropriée et en utilisant le fuseau horaire approprié. Vous pouvez ensuite définir chaque champ de chaque calendrier sur heure, minute, seconde et milliseconde sur 0 et comparer les temps résultants. Certainement icky par rapport à la solution Joda cependant :)
La partie fuseau horaire est importante: elle java.util.Date
est toujours basée sur UTC. Dans la plupart des cas où je suis intéressé par une date, c'est une date dans un fuseau horaire spécifique . Cela vous obligera à utiliser Calendar
Joda Time (à moins que vous ne teniez compte du fuseau horaire vous-même, ce que je ne recommande pas.)
Référence rapide pour les développeurs Android
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Exemple de code (exemple)
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 est presque omniprésent. Et alors?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Si vous voulez vraiment utiliser le java.util.Date, vous feriez quelque chose comme ceci:
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, en utilisant un calendrier à la place (préféré, car getYear () et autres sont obsolètes)
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);
}
}
Ma préférence serait d'utiliser directement la bibliothèque de Jodajava.util.Date
, car Joda fait une distinction entre la date et l'heure (voir les classes YearMonthDay et DateTime ).
Cependant, si vous souhaitez l'utiliser, java.util.Date
je vous suggère d'écrire une méthode utilitaire; par exemple
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();
}
Des opinions sur cette alternative?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==
opérateur ne retourne pas nécessairement true
pour des chaînes équivalentes (utilisation equals()
). Vous ne pouvez certainement pas non plus utiliser les autres opérateurs de comparaison que vous avez mentionnés.
sdf.format(date1).compareTo(sdf.format(date2));
.
Moi aussi, je préfère Joda Time , mais voici une alternative:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
ÉDITER
Je mets le truc UTC ci-dessous au cas où vous auriez besoin de comparer les dates pour un fuseau horaire spécifique autre que UTC. Si vous avez un tel besoin, alors je vous conseille vraiment d'aller chercher 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()
)
Évitez les anciennes classes de date et heure héritées, comme Date
& Calendar
, désormais supplantées par les classes java.time.
A java.util.Date
représente un moment sur la timeline en UTC. L'équivalent dans java.time est Instant
. Vous pouvez convertir en utilisant de nouvelles méthodes ajoutées à la classe héritée.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Vous souhaitez comparer par date. Un fuseau horaire est crucial pour déterminer une date. Pour un moment donné, la date varie dans le monde par zone. Par exemple, quelques minutes après minuit à Paris, la France est un nouveau jour alors qu'hier encore à Montréal Québec .
Spécifiez un nom de fuseau horaire correct au format continent/region
, tel que America/Montreal
,Africa/Casablanca
ou Pacific/Auckland
. N'utilisez jamais l'abréviation de 3 à 4 lettres telle que EST
ou IST
car ce ne sont pas de véritables fuseaux horaires, ni standardisés, ni même uniques (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Appliquez le ZoneId
à Instant
pour obtenir un ZonedDateTime
.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
le LocalDate
classe représente une valeur de date uniquement sans heure et sans fuseau horaire. Nous pouvons extraire un LocalDate
de a ZonedDateTime
, éliminant efficacement la partie de l'heure.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Comparez maintenant, en utilisant des méthodes telles que isEqual
, isBefore
et isAfter
.
Boolean sameDate = localDate1.isEqual( localDate2 );
Découvrez ce code en direct sur 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érique / Montréal] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [Amérique / Montréal]
localDate1: 25/03/2017 | localDate2: 2017-03-24
sameDate: false
Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date
, Calendar
, &SimpleDateFormat
.
Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers java.time classes .
Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .
Où obtenir les classes java.time?
Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour de futurs ajouts possibles à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval
, YearWeek
, YearQuarter
et plus .
Apache commons-utils déjà mentionné :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
vous donne un objet Date contenant uniquement la date, sans heure, et vous pouvez le comparer avec Date.compareTo
J'ai bien peur qu'il n'y ait pas de méthode pour comparer deux dates qui pourraient être appelées "faciles" ou "simples".
Lorsque vous comparez deux instances de temps avec n'importe quelle sorte de précision réduite (par exemple en comparant simplement des dates), vous devez toujours prendre en compte la façon dont le fuseau horaire affecte la comparaison.
Si date1
spécifie un événement qui s'est produit dans le fuseau horaire +2 et date2
spécifie un événement qui s'est produit dans EST, par exemple, vous devez prendre soin de bien comprendre les implications de la comparaison.
Votre objectif est-il de déterminer si les deux événements se sont produits à la même date dans leurs propres fuseaux horaires respectifs? Ou avez-vous besoin de savoir si les deux dates tombent dans la même date de calendrier dans un fuseau horaire spécifique (UTC ou votre TZ locale, par exemple).
Une fois que vous avez compris ce que vous essayez de comparer, il suffit de placer le triple de l'année et du mois dans un fuseau horaire approprié et de faire la comparaison.
Le temps Joda peut rendre l'opération de comparaison réelle beaucoup plus propre, mais la sémantique de la comparaison est toujours quelque chose que vous devez comprendre vous-même.
Si vous utilisez Java 8, vous devez utiliser les java.time.*
classes pour comparer les dates - il est préférable aux diversjava.util.*
classes
par exemple; 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 vous souhaitez simplement comparer seulement deux dates sans heure, le code suivant peut vous aider:
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
}
Voici une solution de ce 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.");
c'est-à-dire que vous pouvez voir si le décalage horaire en millisecondes est inférieur à la durée d'un jour.
Je ne sais pas si c'est nouveau, ou je pense, mais je vous montre ce que j'ai fait
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);
Vérifiez simplement DAY_OF_YEAR en combinaison avec la propriété YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
ÉDITER:
Maintenant, nous pouvons utiliser la puissance des fonctions d'extension 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();
}
Cela vous aidera à comparer les dates sans tenir compte de l'heure.
En utilisant getDateInstance de SimpleDateFormat, nous ne pouvons comparer que deux objets de date sans heure. Exécutez le code ci-dessous.
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));
}
Utilisation de 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)
.
Une autre méthode de comparaison simple basée sur les réponses ici et les conseils de mon 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 : Selon @Jonathan Drapeau, le code ci-dessus échoue dans certains cas (j'aimerais voir ces cas, s'il vous plaît) et il a suggéré ce qui suit si je comprends bien:
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());
}
Veuillez noter que la classe Date est obsolète car elle ne se prêtait pas à l'internationalisation. La classe Calendar est utilisée à la place!
getXxx
méthodes obsolètes de Date
.
Tout d'abord, sachez que cette opération dépend du fuseau horaire. Choisissez donc si vous voulez le faire en UTC, dans le fuseau horaire de l'ordinateur, dans votre propre fuseau horaire préféré ou où. Si vous n'êtes pas encore convaincu que cela compte, consultez mon exemple au bas de cette réponse.
Étant donné que votre question n'est pas très claire à ce sujet, je suppose que vous avez une classe avec un champ d'instance représentant un point dans le temps et l'implémentation Comparable
, et que vous souhaitez que l'ordre naturel de vos objets soit par date, mais pas l'heure , de ce domaine. Par exemple:
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.time
Cours Java 8J'ai pris la liberté de changer le type de votre champ d'instance de Date
à Instant
, la classe correspondante en Java 8. Je promets de revenir au traitement de Date
ci - dessous. J'ai également ajouté une constante de fuseau horaire. Vous pouvez le définir sur ZoneOffset.UTC
ou ZoneId.of("Europe/Stockholm")
ou sur ce que vous jugez approprié (le définir sur un ZoneOffset
works parce qu'il ZoneOffset
s'agit d'une sous-classe de ZoneId
).
J'ai choisi de montrer la solution en utilisant les classes Java 8. Vous avez demandé le moyen le plus simple, non? :-) Voici la compareTo
méthode que vous avez demandée:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Si vous n'avez jamais besoin du temps when
, il est bien sûr plus facile de déclarer when
un LocalDate
et d'ignorer toutes les conversions. Ensuite, nous n'avons plus à nous soucier du fuseau horaire.
Supposons maintenant que, pour une raison quelconque, vous ne puissiez pas déclarer votre when
champ Instant
ou que vous souhaitiez le conserver à l'ancienne Date
. Si vous pouvez toujours utiliser Java 8, convertissez-le simplement Instant
, puis procédez comme avant:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
De même pour o.when
.
Si vous ne pouvez pas utiliser java 8, il existe deux options:
Calendar
ouSimpleDateFormat
.La réponse d'Adamski vous montre comment retirer la partie du temps d'une Date
utilisation de la Calendar
classe. Je vous suggère d'utiliser getInstance(TimeZone)
pour obtenir l' Calendar
instance pour le fuseau horaire que vous souhaitez. Comme alternative, vous pouvez utiliser l'idée de la seconde moitié de la réponse de Jorn .
L'utilisation SimpleDateFormat
est vraiment une manière indirecte d'utiliser Calendar
car a SimpleDateFormat
contient un Calendar
objet. Cependant, vous pouvez trouver cela moins gênant que l'utilisation Calendar
directe:
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));
}
Cela a été inspiré par la réponse de Rob .
Pourquoi devons-nous choisir un fuseau horaire spécifique? Disons que nous voulons comparer deux fois le temps en UTC le 24 mars à 0h00 (minuit) et à 12h00 (midi). Si vous le faites en CET (disons Europe / Paris), ils sont 1 h et 13 h le 24 mars, c'est-à-dire à la même date. À New York (heure avancée de l'Est), ils sont 20h00 le 23 mars et 8h00 le 24 mars, c'est-à-dire pas à la même date. Cela fait donc une différence quel fuseau horaire vous choisissez. Si vous vous fiez uniquement à la valeur par défaut de l'ordinateur, vous risquez d'être surpris lorsque quelqu'un essaie d'exécuter votre code sur un ordinateur à un autre endroit dans ce monde globalisé.
Lien vers ThreeTen Backport, le backport des classes de date et d'heure Java 8 vers Java 6 et 7: http://www.threeten.org/threetenbp/ .
Ma proposition:
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
c'est le moyen le plus simple de résoudre ce problème.
J'ai résolu cela en comparant par horodatage:
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
}
J'évite d'utiliser Joda Time car sur Android utilise un immense espace. Questions de taille. ;)
Une autre solution utilisant Java 8 et Instant, utilise la méthode truncatedTo
Renvoie une copie de cet instant tronqué à l'unité spécifiée.
Exemple:
@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)
}
C'est ce qui a fonctionné pour moi:
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 }