web-dev-qa-db-fra.com

Différence en jours entre deux dates en Java?

J'ai besoin de trouver le nombre de jours entre deux dates : l'un provient d'un rapport et l'autre est la date actuelle. Mon extrait:

  int age=calculateDifference(agingDate, today);

Ici calculateDifference est une méthode privée, agingDate et today sont des objets Date, pour votre clarification. J'ai suivi deux articles d'un Java forum, Discussion 1 / Discussion 2 .

Cela fonctionne bien dans un programme autonome, bien que lorsque j'inclue ceci dans ma logique pour lire dans le rapport, j'obtiens une différence inhabituelle de valeurs.

Pourquoi cela se produit-il et comment puis-je le réparer?

EDIT:

Je reçois un plus grand nombre de jours que le nombre réel de jours.

public static int calculateDifference(Date a, Date b)
{
    int tempDifference = 0;
    int difference = 0;
    Calendar earlier = Calendar.getInstance();
    Calendar later = Calendar.getInstance();

    if (a.compareTo(b) < 0)
    {
        earlier.setTime(a);
        later.setTime(b);
    }
    else
    {
        earlier.setTime(b);
        later.setTime(a);
    }

    while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
    {
        tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
    {
        tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    return difference;
}

Remarque:

Malheureusement, aucune des réponses ne m'a aidé à résoudre le problème. J'ai accompli ce problème à l'aide de la bibliothèque Joda-time .

80
Venkat

Je suggérerais que vous utilisiez l'excellente bibliothèque Joda Time à la place de la version défaillante de Java.util.Date et de ses amis. Vous pouvez simplement écrire

import Java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;

Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th 
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34
148
Adam Schmideg

Je suis peut-être trop tard pour participer au jeu, mais que diable hein? :)

Pensez-vous que ceci est un problème de threading? Comment utilisez-vous le résultat de cette méthode par exemple? OU

Pouvons-nous changer votre code pour faire quelque chose d'aussi simple que:

Calendar calendar1 = Calendar.getInstance();
    Calendar calendar2 = Calendar.getInstance();
    calendar1.set(<your earlier date>);
    calendar2.set(<your current date>);
    long milliseconds1 = calendar1.getTimeInMillis();
    long milliseconds2 = calendar2.getTimeInMillis();
    long diff = milliseconds2 - milliseconds1;
    long diffSeconds = diff / 1000;
    long diffMinutes = diff / (60 * 1000);
    long diffHours = diff / (60 * 60 * 1000);
    long diffDays = diff / (24 * 60 * 60 * 1000);
    System.out.println("\nThe Date Different Example");
    System.out.println("Time in milliseconds: " + diff
 + " milliseconds.");
    System.out.println("Time in seconds: " + diffSeconds
 + " seconds.");
    System.out.println("Time in minutes: " + diffMinutes 
+ " minutes.");
    System.out.println("Time in hours: " + diffHours 
+ " hours.");
    System.out.println("Time in days: " + diffDays 
+ " days.");
  }
48
Suji

Le diff/(24 * etc) ne prend pas en compte le fuseau horaire. Par conséquent, si votre fuseau horaire par défaut contient un heure d’heure, le calcul peut être annulé.

Ce lien a une petite implémentation intéressante.

Voici la source du lien ci-dessus au cas où le lien tomberait:

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  //assert: startDate must be before endDate  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  

et

/** Using Calendar - THE CORRECT (& Faster) WAY**/  
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
  //assert: startDate must be before endDate  
  int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;  
  long endInstant = endDate.getTimeInMillis();  
  int presumedDays = 
    (int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);  
  Calendar cursor = (Calendar) startDate.clone();  
  cursor.add(Calendar.DAY_OF_YEAR, presumedDays);  
  long instant = cursor.getTimeInMillis();  
  if (instant == endInstant)  
    return presumedDays;

  final int step = instant < endInstant ? 1 : -1;  
  do {  
    cursor.add(Calendar.DAY_OF_MONTH, step);  
    presumedDays += step;  
  } while (cursor.getTimeInMillis() != endInstant);  
  return presumedDays;  
}
23
Mad_troll

Java.time

Dans Java 8 et versions ultérieures, utilisez le framework Java.time ( Tutorial ).

Duration

La classe Duration représente une période de temps exprimée en nombre de secondes et une fraction de seconde. Il peut compter des jours, des heures, des minutes et des secondes.

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());

ChronoUnit

Si tout ce dont vous avez besoin est le nombre de jours, vous pouvez également utiliser le ChronoUnitenum . Notez que les méthodes de calcul renvoient un long plutôt que int.

long days = ChronoUnit.DAYS.between( then, now );
16
Vitalii Fedorenko
import Java.util.Calendar;
import Java.util.Date;

public class Main {
    public static long calculateDays(String startDate, String endDate)
    {
        Date sDate = new Date(startDate);
        Date eDate = new Date(endDate);
        Calendar cal3 = Calendar.getInstance();
        cal3.setTime(sDate);
        Calendar cal4 = Calendar.getInstance();
        cal4.setTime(eDate);
        return daysBetween(cal3, cal4);
    }

    public static void main(String[] args) {
        System.out.println(calculateDays("2012/03/31", "2012/06/17"));

    }

    /** Using Calendar - THE CORRECT WAY**/
    public static long daysBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long daysBetween = 0;
        while (date.before(endDate)) {
            date.add(Calendar.DAY_OF_MONTH, 1);
            daysBetween++;
        }
        return daysBetween;
    }
}
13
Soumyarup Dasgupta

Cela dépend de ce que vous définissez comme différence. Pour comparer deux dates à minuit, vous pouvez le faire.

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;
13
Peter Lawrey

Solution utilisant la différence entre les millisecondes et l’arrondi correct pour les dates d’été:

public static long daysDiff(Date from, Date to) {
    return daysDiff(from.getTime(), to.getTime());
}

public static long daysDiff(long from, long to) {
    return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}

Une note: bien sûr, les dates doivent être dans un fuseau horaire.

Le code important:

Math.round( (to - from) / 86400000D )

Si vous ne voulez pas rond, vous pouvez utiliser les dates UTC,

9
angelcervera

Illustration du problème: (mon code calcule le delta en semaines, mais le même problème s’applique au delta en jours)

Voici une implémentation très raisonnable:

public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;

static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
    long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
    int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
    return deltaInWeeks; 
}

Mais ce test échouera:

public void testGetDeltaInWeeks() {
    delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
    assertEquals("weeks between Feb23 and Mar09", 2, delta);
}

La raison est:

Lun 09 mars 00:00:00 EDT 2009 = 1 236 571 200 000
Lundi 23 février 00:00:00 HNE 2009 = 1 235 365 200 000
MillisPerWeek = 604 800 000
Ainsi,
(Mars 2009 - 23 février)/MillisPerWeek =
1 206 000 000/604 800 000 = 1,994 ...

mais quiconque regarde un calendrier serait d'accord pour dire que la réponse est 2.

4
KennethB

J'utilise cette fonction:

DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days

ma fonction:

import Java.util.Date;

public long DATEDIFF(String date1, String date2) {
        long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
        long days = 0l;
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");

        Date dateIni = null;
        Date dateFin = null;        
        try {       
            dateIni = (Date) format.parse(date1);
            dateFin = (Date) format.parse(date2);
            days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;                        
        } catch (Exception e) {  e.printStackTrace();  }   

        return days; 
     }
3
cesin

Basé sur la réponse de @ Mad_Troll, j'ai développé cette méthode.

J'ai exécuté environ 30 cas de test, c'est la seule méthode qui gère correctement les fragments d'heure sous jour.

Exemple: Si vous passez maintenant et maintenant + 1 milliseconde qui est toujours le même jour. Faire 1-1-13 23:59:59.098 à 1-1-13 23:59:59.099 renvoie 0 jour correctement; attribuer des autres méthodes affichées ici ne le fera pas correctement.

Il est à noter qu’il n’importe pas de savoir comment vous les avez insérés. Si votre date de fin est antérieure à votre date de début, elle sera prise en compte à rebours.

/**
 * This is not quick but if only doing a few days backwards/forwards then it is very accurate.
 *
 * @param startDate from
 * @param endDate   to
 * @return day count between the two dates, this can be negative if startDate is after endDate
 */
public static long daysBetween(@NotNull final Calendar startDate, @NotNull final Calendar endDate) {

    //Forwards or backwards?
    final boolean forward = startDate.before(endDate);
    // Which direction are we going
    final int multiplier = forward ? 1 : -1;

    // The date we are going to move.
    final Calendar date = (Calendar) startDate.clone();

    // Result
    long daysBetween = 0;

    // Start at millis (then bump up until we go back a day)
    int fieldAccuracy = 4;
    int field;
    int dayBefore, dayAfter;
    while (forward && date.before(endDate) || !forward && endDate.before(date)) {
        // We start moving slowly if no change then we decrease accuracy.
        switch (fieldAccuracy) {
            case 4:
                field = Calendar.MILLISECOND;
                break;
            case 3:
                field = Calendar.SECOND;
                break;
            case 2:
                field = Calendar.MINUTE;
                break;
            case 1:
                field = Calendar.HOUR_OF_DAY;
                break;
            default:
            case 0:
                field = Calendar.DAY_OF_MONTH;
                break;
        }
        // Get the day before we move the time, Change, then get the day after.
        dayBefore = date.get(Calendar.DAY_OF_MONTH);
        date.add(field, multiplier);
        dayAfter = date.get(Calendar.DAY_OF_MONTH);

        // This shifts lining up the dates, one field at a time.
        if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
            fieldAccuracy--;
        // If day has changed after moving at any accuracy level we bump the day counter.
        if (dayBefore != dayAfter) {
            daysBetween += multiplier;
        }
    }
    return daysBetween;
}

Vous pouvez enlever le @NotNull annotations, celles-ci sont utilisées par Intellij pour analyser le code à la volée

2
Chris.Jenkins

Regardez les méthodes getFragmentInDays de cette classe Apache commons-lang DateUtils.

2
ccpizza

Cent lignes de code pour cette fonction de base ???

Juste une méthode simple:

protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
    return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24); 
    // MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}
1
joro

Trois Dix Extra

Le Réponse de Vitalii Fedorenko est correct. Il décrit comment effectuer ce calcul de manière moderne avec Java.time classes ( Duration = & ChronoUnit ) intégré à Java 8 et versions ultérieures (et port backporté à Java = 6 et 7 et sur Android ).

Days

Si vous utilisez régulièrement plusieurs jours dans votre code, vous pouvez remplacer de simples entiers par l'utilisation d'une classe. La classe Days peut être trouvée dans le projet ThreeTen-Extra , une extension de Java.time et un terrain d'expansion pour de futurs ajouts possibles à Java.time. La classe Days fournit un moyen sûr de représenter un nombre de jours dans votre application. La classe comprend des constantes pratiques pour ZERO et ONE .

Compte tenu de l'ancien démodé Java.util.Date objets de la question, convertissez-les d’abord en modernes Java.time.Instant objets. Les anciennes classes date-heure ont récemment ajouté des méthodes pour faciliter la conversion en Java.time, telles que Java.util.Date::toInstant .

Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant();  // Exclusive.

Passez les deux objets Instant à la méthode factory pour org.threeten.extra.Days .

Dans l'implémentation actuelle (2016-06), il s'agit d'un appel de wrapper Java.time.temporal.ChronoUnit.DAYS.between , lisez le doc de la classe ChronoUnit pour plus de détails. Pour être clair: toutes les majuscules DAYS sont dans l’énumération ChronoUnit alors que initial-cap Days est une classe de ThreeTen-Extra.

Days days = Days.between( start , stop );

Vous pouvez passer ces objets Days autour de votre propre code. Vous pouvez sérialiser en String au format standard ISO 8601 en appelant toString. Ce format de PnD utilise un P pour marquer le début et D signifie "jours", avec un nombre de jours entre les deux. Les classes Java.time et ThreeTen-Extra utilisent ces formats standard par défaut lors de la génération et de l'analyse de chaînes représentant des valeurs date-heure.

String output = days.toString();

P3D

Days days = Days.parse( "P3D" );  
1
Basil Bourque
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
user1091978

Vous dites que cela "fonctionne bien dans un programme autonome", mais que vous obtenez des "valeurs de différence inhabituelles" lorsque vous "incluez ceci dans ma logique pour lire un rapport". Cela suggère que votre rapport contient des valeurs pour lesquelles il ne fonctionne pas correctement et que votre programme autonome ne les contient pas. Au lieu d'un programme autonome, je suggère un cas de test. Ecrivez un scénario de test à la manière d'un programme autonome, en sous-classant à partir de la classe TestCase de JUnit. Vous pouvez maintenant exécuter un exemple très spécifique, en connaissant la valeur à laquelle vous vous attendez (et ne le donnez pas aujourd'hui pour la valeur de test, car la situation évolue dans le temps). Si vous entrez les valeurs que vous avez utilisées dans le programme autonome, vos tests réussiront probablement. C'est génial - vous voulez que ces cas continuent à fonctionner. Maintenant, ajoutez une valeur à votre rapport, une valeur qui ne fonctionne pas correctement. Votre nouveau test va probablement échouer. Déterminez pourquoi il échoue, corrigez-le et passez au vert (tous les tests réussissent). Exécutez votre rapport. Voir ce qui est encore cassé; écrire un test; le faire passer. Bientôt, vous constaterez que votre rapport fonctionne.

1
Carl Manaster

Ce code calcule les jours entre 2 dates.

    static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
    static final String DATE_FORMAT = "dd-MM-yyyy";
    public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
    Date fromDate;
    Date toDate;
    fromDate = format.parse(fromDateStr);
    toDate = format.parse(toDateStr);
    return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}
0
Kayvan Tehrani

Vous devez utiliser la bibliothèque Joda Time car Java Date util retourne parfois des valeurs erronées.

Joda vs Java Util Date

Par exemple, jours entre hier (jj-mm-aaaa, 12-07-2016) et le premier jour de l'année en 1957 (jj-mm-aaaa, 01-01-1957):

public class Main {

public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date date = null;
    try {
        date = format.parse("12-07-2016");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    //Try with Joda - prints 21742
    System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
    //Try with Java util - prints 21741
    System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));    
}


private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
    DateTime jodaDateTime = new DateTime(date);
    DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
    DateTime y1957 = formatter.parseDateTime("01-01-1957");

    return Days.daysBetween(y1957 , jodaDateTime).getDays();
}

private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date y1957 = null;
    try {
        y1957 = format.parse("01-01-1957");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}

Je vous conseille donc vivement d'utiliser la bibliothèque Joda Time.

0
Šime Tokić

Si vous recherchez une solution qui renvoie le nombre approprié ou des jours, par exemple, 11/30/2014 23:59 et 12/01/2014 00:01 voici une solution utilisant Joda Time.

private int getDayDifference(long past, long current) {
    DateTime currentDate = new DateTime(current);
    DateTime pastDate = new DateTime(past);
    return currentDate.getDayOfYear() - pastDate.getDayOfYear();
} 

Cette implémentation renverra 1 comme une différence en jours. La plupart des solutions publiées ici calculent la différence en millisecondes entre deux dates. Cela signifie que 0 serait retourné car il n'y a que 2 minutes de différence entre ces deux dates.

0
tomrozb

J'ai déjà écrit à ce sujet. Ceci est un repost de Calcul de la différence entre deux Java) .

public int getDiffernceInDays(long timeAfter, long timeBefore) {
    Calendar calendarAfter = Calendar.getInstance();
    calendarAfter.setTime(new Date(timeAfter));

    Calendar calendarNewAfter = Calendar.getInstance();
    calendarNewAfter.set(calendarAfter.get(Calendar.YEAR), calendarAfter.get(Calendar.MONTH), calendarAfter.get(Calendar.DAY_OF_MONTH));

    Calendar calendarBefore = Calendar.getInstance();
    calendarBefore.setTime(new Date(timeBefore));

    Calendar calendarNewBefore = Calendar.getInstance();
    calendarNewBefore.set(calendarBefore.get(Calendar.YEAR), calendarBefore.get(Calendar.MONTH), calendarBefore.get(Calendar.DAY_OF_MONTH));

    return (int) ((calendarNewAfter.getTime().getTime() - calendarNewBefore.getTime().getTime()) / (24 * 60 * 60 * 1000));
}
0
M. S.