Calculer le temps écoulé en Java / Groovy

J’ai…

Date start = new Date() ... ... ... Date stop = new Date() 

J’aimerais connaître les années, les mois, les jours, les heures, les minutes et les secondes écoulés entre ces deux dates.

Je vais affiner la question.

Je veux simplement obtenir le temps écoulé, en tant que mesure absolue, c’est-à-dire sans tenir compte des années bissextiles, des jours de chaque mois, etc.

Ainsi, je pense qu’il est impossible d’obtenir les années et les mois écoulés, tout ce que je peux obtenir, c’est des jours, des heures, des minutes et des secondes.

Plus précisément, je veux dire qu’une certaine tâche a duré par exemple

 20 sec 13 min, 4 sec 2 h, 10 min, 2 sec 4 d, 4 h, 2 min, 2 sec 

Alors pardonnez mon manque de précision.

Je viens de découvrir cette solution rapide basée sur Groovy:

 import groovy.time.TimeCategory import groovy.time.TimeDuration TimeDuration td = TimeCategory.minus( stop, start ) println td 

Vous pouvez faire tout cela avec la division et le mod.

 long l1 = start.getTime(); long l2 = stop.getTime(); long diff = l2 - l1; long secondInMillis = 1000; long minuteInMillis = secondInMillis * 60; long hourInMillis = minuteInMillis * 60; long dayInMillis = hourInMillis * 24; long elapsedDays = diff / dayInMillis; diff = diff % dayInMillis; long elapsedHours = diff / hourInMillis; diff = diff % hourInMillis; long elapsedMinutes = diff / minuteInMillis; diff = diff % minuteInMillis; long elapsedSeconds = diff / secondInMillis; 

Cela devrait vous donner toutes les informations que vous avez demandées.

EDIT: Comme les gens semblent confus, non, cela ne prend pas en compte les années bissextiles ou les changements d’heure d’été. C’est le temps écoulé pur, qui est demandé par opensa.

Pas si facile avec l’API Date standard.

Vous pourriez vouloir regarder Joda Time ou JSR-310 à la place.

Je ne suis pas un expert à Joda, mais je pense que le code serait:

 Interval interval = new Interval(d1.getTime(), d2.getTime()); Period period = interval.toPeriod(); System.out.printf( "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", period.getYears(), period.getMonths(), period.getDays(), period.getHours(), period.getMinutes(), period.getSeconds()); 

En ce qui concerne JodaTime, je viens juste de commencer. Merci à l’intervenant qui l’a suggéré. Voici une version plus condensée du code Joda proposé:

 Period period = new Period(d1.getTime(), d2.getTime()); System.out.printf( "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", period.getYears(), period.getMonths(), period.getDays(), period.getHours(), period.getMinutes(), period.getSeconds()); 

(Je ne sais pas si cela aide la question initiale, mais certainement les chercheurs).

Eh bien, depuis Java 1.5, vous devez utiliser TimeUnit.

Voici un exemple simple et clair pour cela. Je pense que dans groovy ça pourrait être plus court (comme toujours).

 /** * Formats a given {@link Date} to display a since-then timespan. * @param created * @return Ssortingng with a format like "3 minutes ago" */ public static Ssortingng getElapsedTime(Date created) { long duration = System.currentTimeMillis() - created.getTime(); long seconds = TimeUnit.MILLISECONDS.toSeconds(duration); long days = TimeUnit.MILLISECONDS.toDays(duration); long hours = TimeUnit.MILLISECONDS.toHours(duration); long minutes = TimeUnit.MILLISECONDS.toMinutes(duration); if (days > 0) { return days + " days"; } if (hours > 0) { return hours + " hrs"; } if (minutes > 0) { return minutes + " minutes"; } return seconds + " seconds"; } 

Oh et éviter les retours multiples s’il vous plaît;)

En fait, en ce qui concerne les réponses ci-dessus à propos de Joda-Time .

Il existe un moyen plus simple de le faire avec la classe Period de Joda-Time:

 Period period = new Period(startDate, endDate); System.out.println(PeriodFormat.getDefault().print(period)); 

Pour personnaliser la sortie, consultez les classes PeriodFormat , PeriodFormatter et PeriodFormatterBuilder .

tl; dr

 Duration.between( then , Instant.now() ) 

Utiliser java.time

La méthode moderne utilise les classes java.time qui supplantent les anciennes classes fastidieuses.

Au lieu de Date , utilisez Instant . La classe Instant représente un moment sur la timeline en UTC avec une résolution de nanosecondes (jusqu’à neuf (9) chiffres d’une fraction décimale).

 Instant then = Instant.now(); … Instant now = Instant.now(); 

Utilisez la classe Duration pendant une période sans attachement à la chronologie, avec une résolution des heures-minutes-minutes-secondes-nanos.

 Duration d = Duration.between( then , now ); 

Pour une période de temps avec résolution d’années-mois-jours, utilisez la classe Period .

Générer une chaîne est le format standard ISO 8601 pour les durées : PnYnMnDTnHnMnS . Le P marque le début. Le T sépare les années-mois-jours des heures-minutes-secondes. Donc, deux heures et demie, c’est PT2H30M .

 Ssortingng output = d.toSsortingng(); 

Dans Java 9 et les toDaysPart ultérieures, vous pouvez accéder aux composants individuels avec des méthodes toDaysPart , toHoursPart , etc.


À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes de date / heure héritées telles que java.util.Date , Calendar et SimpleDateFormat .

Le projet Joda-Time , maintenant 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 .

Où obtenir les classes java.time?

  • Java SE 8 et SE 9 et versions ultérieures
    • Intégré
    • Partie de l’API Java standard avec une implémentation intégrée.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • Java SE 6 et SE 7
    • Une grande partie de la fonctionnalité java.time est transférée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus) pour Android en particulier.
    • Voir Comment utiliser ThreeTenABP… .

Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d’essai pour d’éventuels ajouts futurs à java.time. Vous pouvez y trouver des classes utiles telles que Interval , YearWeek , YearQuarter , etc.

Hmm, si je comprends ce que vous demandez, vous voulez savoir si:

début = 1er janvier 2001, 10h00: 00h00 et

arrêt = 6 janvier 2003, 12h01: 00h

vous voulez une réponse de 2 ans, 0 mois, 5 jours, 2 heures, 1 minute

Malheureusement, c’est une réponse spécieuse. Et si les dates étaient le 2 janvier et le 31 janvier? Serait-ce 29 jours? Ok, mais du 2 février au 2 mars est 28 (29) jours, mais serait listé comme 1 mois?

La durée sur autre chose que les secondes ou éventuellement les jours est variable sans connaître le contexte puisque les mois et les années sont de longueurs différentes. La différence entre 2 dates doit être en unités statiques, facilement calculables à partir de stop.getTime () – start.getTime () (différence en millisec)

En dehors de la formidable API JodaTime que je recommande, la meilleure alternative Java que vous pouvez avoir est java.util.Calendar . Il est fastidieux de travailler avec ça (c’est un euphémisme … regardez les exemples de lignes simples JodaTime ici), mais vous pouvez aussi calculer le temps écoulé avec. La clé est que vous devez utiliser le Calendar#add() dans une boucle pour obtenir la valeur écasting pendant des années, des mois et des jours pour tenir compte des jours bissextiles, des années et des siècles. Vous ne devez pas les calculer à partir des (milli) secondes.

Voici un exemple de base:

 import java.util.Calendar; public class Test { public static void main(Ssortingng[] args) throws Exception { Calendar start = Calendar.getInstance(); start.set(1978, 2, 26, 12, 35, 0); // Just an example. Calendar end = Calendar.getInstance(); Integer[] elapsed = new Integer[6]; Calendar clone = (Calendar) start.clone(); // Otherwise changes are been reflected. elapsed[0] = elapsed(clone, end, Calendar.YEAR); clone.add(Calendar.YEAR, elapsed[0]); elapsed[1] = elapsed(clone, end, Calendar.MONTH); clone.add(Calendar.MONTH, elapsed[1]); elapsed[2] = elapsed(clone, end, Calendar.DATE); clone.add(Calendar.DATE, elapsed[2]); elapsed[3] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 3600000; clone.add(Calendar.HOUR, elapsed[3]); elapsed[4] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 60000; clone.add(Calendar.MINUTE, elapsed[4]); elapsed[5] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 1000; System.out.format("%d years, %d months, %d days, %d hours, %d minutes, %d seconds", elapsed); } private static int elapsed(Calendar before, Calendar after, int field) { Calendar clone = (Calendar) before.clone(); // Otherwise changes are been reflected. int elapsed = -1; while (!clone.after(after)) { clone.add(field, 1); elapsed++; } return elapsed; } } 

Il devrait imprimer mon âge dès maintenant =)

Oh, je devrais append, vous pouvez “convertir” la Date en Calendar utilisant Calendar#setTime() .

C’est facile; Vous devez définir le bon fuseau horaire

 import java.util.TimeZone; import java.util.logging.Logger; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; public class ImportData { private final static Logger log = Logger.getLogger(ImportData.class.getName()); private final static DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS"); private final static DateTimeFormatter dtfh = DateTimeFormat.forPattern("HH:mm:ss.SSS"); /** * @param args the command line arguments */ public static void main(Ssortingng[] args) throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin")); DateTimeZone.setDefault(DateTimeZone.forID("Europe/Berlin")); // Quotes connection=Quotes.getInstance(); final long start = System.currentTimeMillis(); // do something here ... // connection.importTickdata(); Thread.currentThread().sleep(2000); final long end = System.currentTimeMillis(); log.info("[start] " + dtf.print(start)); log.info("[end] " + dtf.print(end)); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); DateTimeZone.setDefault(DateTimeZone.forID("UTC")); log.info("[duration] " + dtfh.print(end - start)); // connection.logOff(); // System.exit(0); } 

résultats:

 10.11.2010 00:08:12 ImportData main
 INFO: [début] 2010-11-10 00: 08: 10.306
 10.11.2010 00:08:12 ImportData main
 INFO: [fin] 2010-11-10 00: 08: 12.318
 10.11.2010 00:08:12 ImportData main
 INFO: [durée] 00: 00: 02.012

Il ne sert à rien de créer un object Date car tout ce que cela fait est wrap System.currentTimeMillis (). La fonction getTime () déroule simplement l’object Date. Je vous suggère de simplement utiliser cette fonction pour obtenir un long.

Si vous n’avez besoin que d’une seconde précision, c’est bien. Toutefois, si vous voulez une précision inférieure à la milliseconde, utilisez System.nanoTime () pour obtenir le temps écoulé.

C’est une fonction complète que j’ai implémentée basée sur la réponse de Sebastian Celis . Et encore une fois, de son poste – cela ne prend pas les choses comme les années bissextiles ou les changements d’heure d’été en compte. C’est pur temps écoulé .

La sortie est adaptée à mes besoins. Cela ne produit que trois sections significatives. Au lieu de revenir

4 mois, 2 semaines, 3 jours, 7 heures, 28 minutes, 43 secondes

Il renvoie simplement les 3 premiers blocs (voir plus d’exemples à la fin du post) :

4 mois, 2 semaines, 3 jours

Voici le code source complet de la méthode:

 /** * Format milliseconds to elapsed time format * * @param time difference in milliseconds * @return Human readable ssortingng representation - eg. 2 days, 14 hours, 5 minutes */ public static Ssortingng formatTimeElapsedSinceMillisecond(long milisDiff) { if(milisDiff<1000){ return "0 second";} String formattedTime = ""; long secondInMillis = 1000; long minuteInMillis = secondInMillis * 60; long hourInMillis = minuteInMillis * 60; long dayInMillis = hourInMillis * 24; long weekInMillis = dayInMillis * 7; long monthInMillis = dayInMillis * 30; int timeElapsed[] = new int[6]; // Define time units - plural cases are handled inside loop String timeElapsedText[] = {"second", "minute", "hour", "day", "week", "month"}; timeElapsed[5] = (int) (milisDiff / monthInMillis); // months milisDiff = milisDiff % monthInMillis; timeElapsed[4] = (int) (milisDiff / weekInMillis); // weeks milisDiff = milisDiff % weekInMillis; timeElapsed[3] = (int) (milisDiff / dayInMillis); // days milisDiff = milisDiff % dayInMillis; timeElapsed[2] = (int) (milisDiff / hourInMillis); // hours milisDiff = milisDiff % hourInMillis; timeElapsed[1] = (int) (milisDiff / minuteInMillis); // minutes milisDiff = milisDiff % minuteInMillis; timeElapsed[0] = (int) (milisDiff / secondInMillis); // seconds // Only adds 3 significant high valued units for(int i=(timeElapsed.length-1), j=0; i>=0 && j<3; i--){ // loop from high to low time unit if(timeElapsed[i] > 0){ formattedTime += ((j>0)? ", " :"") + timeElapsed[i] + " " + timeElapsedText[i] + ( (timeElapsed[i]>1)? "s" : "" ); ++j; } } // end for - build ssortingng return formattedTime; } // end of formatTimeElapsedSinceMillisecond utility method 

Voici quelques exemples de déclaration de test:

 System.out.println(formatTimeElapsedSinceMillisecond(21432424234L)); // Output: 8 months, 1 week, 1 day System.out.println(formatTimeElapsedSinceMillisecond(87724294L)); // Output: 1 day, 22 minutes, 4 seconds System.out.println(formatTimeElapsedSinceMillisecond(123434L)); // Output: 2 minutes, 3 seconds 

Ceci est une autre fonction similaire, elle ne montre pas les jours, les heures, les minutes, etc. si elle n’est pas nécessaire, changez ses littéraux si nécessaire.

 public class ElapsedTime { public static void main(Ssortingng args[]) { long start = System.currentTimeMillis(); start -= (24 * 60 * 60 * 1000 * 2); start -= (60 * 60 * 1000 * 2); start -= (60 * 1000 * 3); start -= (1000 * 55); start -= 666; long end = System.currentTimeMillis(); System.out.println(elapsedTime(start, end)); } public static Ssortingng elapsedTime(long start, long end) { Ssortingng auxRet = ""; long aux = end - start; long days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0; // days if (aux > 24 * 60 * 60 * 1000) { days = aux / (24 * 60 * 60 * 1000); } aux = aux % (24 * 60 * 60 * 1000); // hours if (aux > 60 * 60 * 1000) { hours = aux / (60 * 60 * 1000); } aux = aux % (60 * 60 * 1000); // minutes if (aux > 60 * 1000) { minutes = aux / (60 * 1000); } aux = aux % (60 * 1000); // seconds if (aux > 1000) { seconds = aux / (1000); } milliseconds = aux % 1000; if (days > 0) { auxRet = days + " days "; } if (days != 0 || hours > 0) { auxRet += hours + " hours "; } if (days != 0 || hours != 0 || minutes > 0) { auxRet += minutes + " minutes "; } if (days != 0 || hours != 0 || minutes != 0 || seconds > 0) { auxRet += seconds + " seconds "; } auxRet += milliseconds + " milliseconds "; return auxRet; } } 

C’est un problème et un algorithme doit être utilisé pour tenir compte des années bissextiles et du nombre exact de mois et de jours à côté des années. Intéressant à quel point il est simple d’utiliser une seule unité de mesure. Par exemple, le nombre total de jours entre deux jours est correct, car il est associé au nombre de jours de rappel après le nombre de mois et d’années calculés, disons deux décennies.

J’y travaille actuellement pour le fournir depuis mon implémentation PML, par exemple sous la forme de:

 unemployed <- date.difference[ From = 2009-07-01, Till = now, YEARS, MONTHS, DAYS ]: yyyy-MM-dd $$-> *unemployed -> date.translate[ YEARS, MONTHS, DAYS ] -> print["Unemployed for:", .] 

Bien entendu, cela serait également utile et nécessaire pour le calcul exact du taux d’intérêt.