Nombre de jours entre deux dates dans Joda-Time


336

Comment puis-je trouver la différence en jours entre deux instances Joda-Time DateTime ? Avec «différence de jours», je veux dire que si le début est le lundi et la fin le mardi, je m'attends à une valeur de retour de 1, indépendamment des heures / minutes / secondes des dates de début et de fin.

Days.daysBetween(start, end).getDays() me donne 0 si le début est le soir et se termine le matin.

J'ai également le même problème avec d'autres champs de date, j'espérais donc qu'il y aurait un moyen générique d'ignorer les champs de moindre importance.

En d'autres termes, les mois entre février et 4 mars seraient également de 1, tout comme les heures entre 14h45 et 15h12. Cependant, la différence horaire entre 14:01 et 14:55 serait de 0.

Réponses:


393

De façon ennuyeuse, la réponse withTimeAtStartOfDay est erronée, mais seulement occasionnellement. Tu veux:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

Il s'avère que "minuit / début de journée" signifie parfois 1 heure du matin (l'heure d'été se produit de cette façon à certains endroits), ce que Days.daysBetween ne gère pas correctement.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Passer par une voie de LocalDatecontournement tout le problème.


Pouvez-vous fournir un exemple de cas avec des données spécifiques où vous pensez que Days.daysBetweenc'est incorrect?
Basil Bourque

Quand vous dites d'utiliser Instant, vous ne parlez pas seulement start.toInstant(), n'est-ce pas?
Patrick M

@PatrickM Oui, je l'étais. À la réflexion, on ne sait pas exactement quelles contraintes cela est censé imposer, je vais donc supprimer cette dernière phrase. Merci!
Alice Purcell

@chrispy daysBetween doc dit qu'il retourne nombre de jours entiers . Dans mon cas 2 jours et 1 heure devraient me retourner 3 jours. Dans votre exemple, il renvoie 2 jours. Existe-t-il un moyen d'y parvenir?
Sujit Joshi

@SujitJoshi J'ai bien peur de ne pas comprendre votre question. Je suggère de poster une question complète sur le débordement de pile et de coller un lien ici pour moi :)
Alice Purcell

186

Days Classe

L'utilisation de la Daysclasse avec la withTimeAtStartOfDayméthode devrait fonctionner:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

1
Merci! J'essayais d'obtenir le comportement de android.text.format.DateUtils.getRelativeTimeSpanString () avec joda et c'était vraiment utile.
gosho_ot_pochivka

1
Monsieur, en référence à ce message, la méthode toDateMidnight()du type DateTime est déconseillée.
Aniket Kulkarni

2
Vous devriez maintenant utiliser .withTimeAtStartOfDay () au lieu de .toDateMidnight ()
bgolson

2
que se passe-t-il si le endest avant le start, renvoie-t-il des jours négatifs?
akhy

7
@akhyar: pourquoi ne l'essayez-vous pas?
Michael Borgwardt

86

vous pouvez utiliser LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

Monsieur, en référence à ce message, la méthode toDateMidnight()du type DateTime est déconseillée.
Aniket Kulkarni

Pourquoi utiliser new LocalDate(date)plus date.toLocalDate()?
SARose

9

tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…ou…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

Pour info, le projet Joda-Time est désormais en mode maintenance , l'équipe conseillant la migration vers les classes java.time .

L'équivalent de Joda-Time DateTimeest ZonedDateTime.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Apparemment, vous voulez compter les jours par date, ce qui signifie que vous voulez ignorer l'heure de la journée. Par exemple, commencer une minute avant minuit et se terminer une minute après minuit devrait se traduire par une seule journée. Pour ce comportement, extrayez un LocalDatede votre ZonedDateTime. La LocalDateclasse représente une valeur de date uniquement sans heure et sans fuseau horaire.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Utilisez l' ChronoUniténumération pour calculer les jours écoulés ou d'autres unités.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Tronquer

Quant à vous demander une façon plus générale de faire ce comptage lorsque vous êtes intéressé par le delta d'heures en heures d'horloge plutôt que par heures complètes en intervalles de soixante minutes, utilisez la truncatedTométhode.

Voici votre exemple de 14h45 à 15h12 le même jour.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

Cela ne fonctionne pas pendant des jours. Utilisez toLocalDate () dans ce cas.


À propos de java.time

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, et SimpleDateFormat.

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .

Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger des objets java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de java.sql.*cours.

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 et versions ultérieures
    • Intégré.
    • Une partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et correctifs mineurs.
  • Java SE 6 et Java SE 7
    • Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Versions ultérieures des implémentations de bundles Android des classes java.time.
    • Pour les versions antérieures d'Android (<26), le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus). Voir Comment utiliser ThreeTenABP… .

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, YearQuarteret plus .

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, YearQuarteret plus .


Votre TL; DR pendant des jours utilisant tronquer tombe en proie au bogue détaillé dans la réponse acceptée, à savoir qu'il est éteint par un lorsque le début de la journée est à 1 heure du matin. Je l'ai mis à jour pour utiliser la bonne réponse que vous avez donnée plus loin, en utilisant toLocalDate.
Alice Purcell

2

La réponse acceptée crée deux LocalDateobjets, qui sont assez chers si vous lisez beaucoup de données. J'utilise ceci:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

En appelant, getMillis()vous utilisez des variables déjà existantes.
MILLISECONDS.toDays()puis, utilise un calcul arithmétique simple, ne crée aucun objet.


1
Cette réponse ne fonctionne que si votre définition de «jours» est exactement de 24 heures sans tenir compte des fuseaux horaires et des dates. Si oui, utilisez cette approche. Sinon, regardez les autres réponses qui traitent des fuseaux horaires.
Basil Bourque

@BasilBourque, vous avez raison, je choisirais une solution basée sur le calcul arithmétique plutôt que de construire des objets coûteux pour chaque appel de méthode, il existe de nombreuses variantes pour de tels calculs, si vous lisez une entrée à partir d'une page Web, peut être correct, mais si vous traitez un fichier journal avec des centaines de milliers de lignes, cela le rend très lent
JBoy

1
Java optimisera l'allocation des objets si votre boucle est chaude. Voir docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Alice Purcell

1

java.time.Period

Utilisez la java.time.Periodclasse pour compter les jours .

Depuis Java 8, le calcul de la différence est plus intuitif en utilisant LocalDate, LocalDateTimepour représenter les deux dates

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);

0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    

-11
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

1
la question est spécifiquement à la recherche de joda-time.
kapad
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.