272 votes

Comparaison de deux dates java.util.Dates pour voir si elles sont dans la même journée

J'ai besoin de comparer deux Date (par exemple date1 y date2 ) et de trouver un boolean sameDay ce qui est vrai pour les deux Date partagent le même jour, et faux s'ils ne le sont pas.

Comment puis-je le faire ? Il semble y avoir un tourbillon de confusion ici... et j'aimerais éviter de faire appel à d'autres dépendances que le JDK si possible.

pour clarifier : si date1 y date2 partagent la même année, le même mois et le même jour, alors sameDay est vraie, sinon elle est fausse. Je me rends compte que cela nécessite la connaissance d'un fuseau horaire... ce serait bien de passer un fuseau horaire, mais je peux vivre avec l'heure GMT ou l'heure locale tant que je sais quel est le comportement.

encore une fois, pour clarifier :

date1 = 2008 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = true

date1 = 2009 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = false

date1 = 2008 Aug 03 12:00:00
date2 = 2008 Jun 03 12:00:00
  => sameDate = false

0 votes

Pour clarifier : vous voulez savoir si deux objets Date tombent le même jour de la semaine ?

0 votes

Voulez-vous comparer la date complète (jour, mois, année) ou seulement le jour du mois ?

1 votes

@Rob : non, le même jour/mois/année... Je vais préciser.

440voto

Michael Borgwardt Points 181658
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);
boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
                  cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);

Notez que le concept de "même jour" n'est pas aussi simple qu'il n'y paraît lorsque des fuseaux horaires différents sont concernés. Le code ci-dessus calculera, pour les deux dates, le jour par rapport au fuseau horaire utilisé par l'ordinateur sur lequel il est exécuté. Si ce n'est pas ce dont vous avez besoin, vous devez passer le(s) fuseau(x) horaire(s) pertinent(s) à la fonction Calendar.getInstance() appelle, après avoir décidé ce que vous entendez exactement par "le même jour".

Et oui, Joda Time's LocalDate rendrait l'ensemble beaucoup plus propre et plus facile (bien que les mêmes difficultés liées aux fuseaux horaires soient présentes).

0 votes

Merci, cela semble faire ce que je veux. Dans mon cas, je compare des dates successives dans une série, donc il semble que je pourrais simplement utiliser des instances de calendrier au lieu d'instances de date dans ma série.

1 votes

@Jason C'est peut-être ou non une bonne idée. Le principal problème avec Calendar est que c'est une classe très lourde avec beaucoup d'état interne, dont une partie est utilisée dans son implémentation equals(). Si vous ne comparez pas vos dates pour l'égalité et ne les mettez pas dans des HashMaps, tout devrait bien se passer.

0 votes

Cool, merci, j'utilise juste la logique de comparaison entre le jour courant et le jour précédent demandée ici. les fonctions "equals" et "hashcode" ne devraient jamais être appelées.

354voto

Binil Thomas Points 6425

Pourquoi pas :

SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));

Vous pouvez également définir le fuseau horaire dans le SimpleDateFormat, si nécessaire.

2 votes

(En fait, j'utilise de toute façon SimpleDateFormat dans mon cas, donc cela semble assez approprié).

9 votes

Je suis en fait surpris de voir ça, mais même d'un point de vue performance, cette SimpleDateFormat est en fait plus rapide que l'autre méthode mentionnée ici qui utilise Calendar s. En moyenne, il prend deux fois moins de temps que le Calendar méthode. (Du moins sur mon système). Félicitations !

0 votes

La plupart des coûts de cette réponse sont liés à la création de SimpleDateFormat. Placez-la dans un champ threadLocal dans un singleton si vous voulez des performances encore meilleures.

171voto

Brent Watson Points 541

J'utilise le paquet "apache commons lang" pour ce faire (à savoir org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects

2 votes

Cela utilise une dépendance externe... mais c'est bon à savoir pour l'avenir.

22 votes

Il suffit de copier la source et de l'appeler un jour :)

0 votes

Mais elle lève une exception pour argument illégal si l'un des jours est nul, ce qui n'est pas idéal dans certains cas.

26voto

AntoineJ Points 81

Vous pouvez éviter les dépendances externes et l'impact sur les performances de l'utilisation de Calendar en calculant le nombre de jours d'utilisation de Calendar. Numéro du jour julien pour chacune des dates, puis de les comparer :

public static boolean isSameDay(Date date1, Date date2) {

    // Strip out the time part of each date.
    long julianDayNumber1 = date1.getTime() / MILLIS_PER_DAY;
    long julianDayNumber2 = date2.getTime() / MILLIS_PER_DAY;

    // If they now are equal then it is the same day.
    return julianDayNumber1 == julianDayNumber2;
}

4 votes

Mais attention, cette ne prend pas en compte les changements de la durée du jour dus à l'heure d'été. Mais alors directement Date n'encapsulent pas la notion de fuseau horaire, il n'y a donc aucun moyen de régler ce problème de manière non arbitraire.

3 votes

C'est de loin la solution la plus rapide. Merci beaucoup, c'est exactement ce que je cherchais, car je dois vérifier un grand nombre de dates ...

2 votes

Petit détail : date1.getTime() ne renvoie pas le numéro du jour julien, mais les millisecondes depuis 1970-01-01. Grande différence.

23voto

Basil Bourque Points 8938

Joda-Time

Pour ce qui est de l'ajout d'une dépendance, j'ai bien peur que les java.util.Date & .Calendar soient tellement mauvais que la première chose que je fais à tout nouveau projet est d'ajouter la bibliothèque Joda-Time. En Java 8, vous pouvez utiliser le nouveau paquet java.time, inspiré de Joda-Time.

Le noyau de Joda-Time est le DateTime classe. Contrairement à java.util.Date, elle comprend le fuseau horaire qui lui est attribué ( DateTimeZone ). Lors de la conversion de j.u.date, attribuez une zone.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTimeQuébec = new DateTime( date , zone );

LocalDate

Une façon de vérifier si deux dates-horaires tombent sur la même date est de convertir en LocalDate objets.

Cette conversion dépend du fuseau horaire attribué. Pour comparer LocalDate les objets, ils doivent avoir été convertis avec la même zone.

Voici une petite méthode utilitaire.

static public Boolean sameDate ( DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1 );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( dt1.getZone() ) );
    Boolean match = ld1.equals( ld2 );
    return match;
}

Il serait préférable d'utiliser un autre argument, spécifiant le fuseau horaire plutôt que de supposer que le premier fuseau horaire de l'objet DateTime doit être utilisé.

static public Boolean sameDate ( DateTimeZone zone , DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1.withZone( zone ) );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( zone ) );
    return ld1.equals( ld2 );
}

Représentation des chaînes de caractères

Une autre approche consiste à créer une représentation en chaîne de la partie date de chaque date-heure, puis à comparer les chaînes.

Là encore, le fuseau horaire attribué est crucial.

DateTimeFormatter formatter = ISODateTimeFormat.date();  // Static method.
String s1 = formatter.print( dateTime1 );
String s2 = formatter.print( dateTime2.withZone( dt1.getZone() )  );
Boolean match = s1.equals( s2 );
return match;

Durée de vie

La solution généralisée consiste à définir un intervalle de temps, puis à demander si cet intervalle contient votre cible. Cet exemple de code est en Joda-Time 2.4. Notez que les classes liées à "midnight" sont dépréciées. Utilisez plutôt les classes withTimeAtStartOfDay méthode. Joda-Time propose trois classes pour représenter un intervalle de temps de différentes manières : Intervalle, Période, et Durée.

Utilisation de l'approche "semi-ouverte" où le début de la portée est inclusif et la fin exclusive.

Le fuseau horaire de la cible peut être différent de celui de l'intervalle.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime target = new DateTime( 2012, 3, 4, 5, 6, 7, timeZone );
DateTime start = DateTime.now( timeZone ).withTimeAtStartOfDay();
DateTime stop = start.plusDays( 1 ).withTimeAtStartOfDay();
Interval interval = new Interval( start, stop );
boolean containsTarget = interval.contains( target );

java.time

Java 8 et les versions ultérieures sont livrés avec le java.time cadre. Inspiré par Joda-Time, défini par la JSR 310, et étendu par le projet ThreeTen-Extra. Voir Tutoriel .

Les créateurs de Joda-Time nous ont demandé de passer à java.time dès que possible. En attendant, Joda-Time reste un projet activement maintenu. Mais attendez-vous à ce que les travaux futurs se déroulent uniquement dans java.time et ThreeTen-Extra plutôt que dans Joda-Time.

Pour résumer java.time en quelques mots An Instant est un moment sur la ligne de temps en UTC. Appliquez un fuseau horaire ( ZoneId ) pour obtenir un ZonedDateTime objet. Pour sortir de la ligne du temps, pour avoir la vague idée indéfinie d'une date-heure, utilisez les classes "locales" : LocalDateTime , LocalDate , LocalTime .

La logique abordée dans la section Joda-Time de cette réponse s'applique à java.time.

L'ancienne classe java.util.Date dispose d'une nouvelle classe toInstant pour la conversion en java.time.

Instant instant = yourJavaUtilDate.toInstant(); // Convert into java.time type.

La détermination d'une date nécessite un fuseau horaire.

ZoneId zoneId = ZoneId.of( "America/Montreal" );

Nous appliquons cet objet fuseau horaire à l'objet Instant pour obtenir un ZonedDateTime . Nous en extrayons une valeur de date uniquement (a LocalDate ) car notre objectif est de comparer des dates (et non des heures, des minutes, etc.).

ZonedDateTime zdt1 = ZonedDateTime.ofInstant( instant , zoneId );
LocalDate localDate1 = LocalDate.from( zdt1 );

Faites de même avec le deuxième java.util.Date l'objet dont nous avons besoin pour la comparaison. Je vais utiliser le moment présent à la place.

ZonedDateTime zdt2 = ZonedDateTime.now( zoneId );
LocalDate localDate2 = LocalDate.from( zdt2 );

Utilisez la fonction spéciale isEqual pour tester la même valeur de date.

Boolean sameDate = localDate1.isEqual( localDate2 );

0 votes

Java.time : Ou vous pouvez simplement faire LocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);

1 votes

@CyberMew Er ? Il n'y a pas fromDateFields() en mon LocalDate classe .

1 votes

Désolé, j'aurais dû être plus clair. Le commentaire est pertinent pour Joda-Time LocalDate classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X