CALENDRIER Java: obtenir la différence entre deux Dates / heures-Off par un
j'ai vu beaucoup de questions et de réponses sur ce sujet, mais aucun n'a abordé mon problème particulier. J'ai étendu la classe de calendrier java (standard--Pas de bibliothèques tierces), et j'ai eu besoin de trouver la différence dans jours entre deux dates arbitraires.
méthode:
- changer l'heure des deux dates à minuit.
- Convertissez les dates en millisecondes.
- trouver le la différence entre les deux dates.
- divisez le résultat par le nombre de millisecondes par jour (24 * 60 * 60 * 1000).
- le résultat devrait être la différence en jours.
et ça l'est parfois, et ça ne l'est pas parfois. Même les tests à la même date peuvent être interrompus d'un seul coup. Ce qui se passe?
7 réponses
Comme suggéré par d'autres utilisateurs, vous devez également définir la valeur en millisecondes des calendriers à zéro de comparer seulement les dates. Ceci peut être réalisé avec l'extrait de code suivant:
someCalendar.set(Calendar.MILLISECOND, 0)
en outre, notez que les changements de fuseau horaire (par exemple passer de l'heure d'hiver à l'heure d'été) peuvent signifier qu'il y a plus ou moins 86.400.000 ms dans une journée.
Le Joda Time Bibliothèque a un très bon soutien pour des problèmes tels:
LocalDate d1 = new LocalDate(calendar1.getTimeInMillis());
LocalDate d2 = new LocalDate(calendar2.getTimeInMillis());
int days = Days.daysBetween(d1, d2).getDays();
mise à jour (feedback de @basil-bourque):
depuis Java 8, la nouvelle bibliothèque de temps java.time
a été introduite, maintenant une option similaire sans dépendances externes est disponible:
int days = Duration.between(calendar1.toInstant(), calendar2.toInstant()).toDays();
tl; dr
ChronoUnit.DAYS.between( then , now )
calculs naïfs
vous supposez dans votre code que chaque jour dure exactement 24 heures. Pas vrai. Des Anomalies telles que heure D'été (HNE) signifient que les jours peuvent varier comme 23 heures de long, 25 heures, ou d'autres nombres. Le sens même d'un fuseau horaire est suivi d'un historique de ces anomalies.
aussi, vous supposez que la journée commence à minuit. Pas vrai. En raison d'anomalies, certains jours commencent à un autre moment de la journée comme 01:00
.
éviter les classes date-heure héritées
vous utilisez les anciennes classes de date ennuyeuses telles que java.util.Date
, java.util.Calendar
, et java.text.SimpleTextFormat
sont maintenant legacy , remplacé par le java.temps des classes.
fuseau horaire
spécifier un nom du fuseau horaire approprié dans le format de continent/region
, tel que America/Montreal
, Africa/Casablanca
, ou Pacific/Auckland
. N'utilisez jamais l'abréviation de 3-4 lettres comme EST
ou IST
car ils sont pas vrai fuseaux horaires, non standardisé, et même pas unique(!).
ne pas étendre java.temps
ne pas étendre (sous-classe) le java.les classes de temps (elles sont marquées final
).
et ne pas généraliser à leurs interfaces pour votre logique d'affaires; s'en tenir aux classes concrètes de ce cadre. Tandis que généraliser fait du sens dans d'autres cadres tels que Java Collections , pas tellement en java.temps.
utilisant java.temps
ce travail est beaucoup plus facile avec Java.les classes de temps.
ZonedDateTime
représente un moment sur la ligne de temps avec une zone de temps assignée ( ZoneId
).
ZoneId z = ZoneId.of( "America/Montreal" );
// of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone)
ZonedDateTime then = ZonedDateTime.of( 2017 , 1 , 23 , 12 , 34 , 56 , 123456789 , z );
ZonedDateTime now = ZonedDateTime.now( z );
le ChronoUnit
enum peut calculer le temps écoulé entre deux moments.
long days = ChronoUnit.DAYS.between( then , now );
Voir ce code à exécuter en direct à IdeOne.com .
puis.toString (): 2017-01-23T12: 34 :56.123456789-05: 00[America/Montreal]
maintenant.toString (): 2017-03-01T21: 26: 04.884-05:00[America/Montreal]
jours: 37
convertissez des instances de java.temps
si vous recevez des objets GregorianCalendar
, convertissez-vous en java.temps en utilisant de nouvelles méthodes ajoutées aux anciennes classes.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;
si vous savez qu'une instance Calendar
est en fait une instance GregorianCalendar
, lancez-la.
GregorianCalendar myGregCal = (GregorianCalendar) myCal ;
Semi-Ouvertes
La java.les classes de temps définissent les périodes de temps par l'approche semi-ouverte où le début est inclusive tandis que la fin est exclusive .
à Propos de java.temps
Le de java.temps framework est construit dans Java 8 et plus tard. Ces classes remplacent les anciennes héritées classes de date-heure telles que java.util.Date
, Calendar
, & SimpleDateFormat
.
le projet Joda-Time , maintenant en mode maintenance , conseille la migration vers le java.temps classe.
pour en savoir plus, voir le Oracle Tutorial . Et rechercher le débordement de la pile pour de nombreux exemples et explications. La spécification est JSR 310 .
où obtenir le java.les classes de temps?
- Java SE 8 et SE 9 et plus tard
- Built-in.
- fait partie de L'API Java standard avec une implémentation groupée.
- Java 9 ajoute des fonctionnalités mineures et des corrections.
- Java SE 6 et SE 7
- une grande partie de la java.la fonctionnalité time est rétroportée à Java 6 & 7 en ThreeTen-Backport .
- Android
le projet ThreeTen-Extra étend java.temps avec des cours supplémentaires. Ce projet est un terrain d'expérimentation pour d'éventuelles futures additions à java.temps. Vous pouvez trouver ici quelques cours utiles tels que: Interval
, YearWeek
, YearQuarter
, et plus .
j'ai écrit une façon beaucoup plus simple pour aborder ce problème, même j'ai fait face au même problème d'avoir un jour supplémentaire pour certaines dates.
Ceci est mon approche actuellement,
public long getDays(long currentTime, long endDateTime) {
Calendar endDateCalendar;
Calendar currentDayCalendar;
//expiration day
endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
endDateCalendar.setTimeInMillis(endDateTime);
endDateCalendar.set(Calendar.MILLISECOND, 0);
endDateCalendar.set(Calendar.MINUTE, 0);
endDateCalendar.set(Calendar.HOUR, 0);
//current day
currentDayCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
currentDayCalendar.setTimeInMillis(currentTime);
currentDayCalendar.set(Calendar.MILLISECOND, 0);
currentDayCalendar.set(Calendar.MINUTE, 0);
currentDayCalendar.set(Calendar.HOUR, 0);
long remainingDays = Math.round((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));
return remainingDays;
}
auparavant j'utilisais ce morceau de ligne, le repos est le même : -
long remainingDays = TimeUnit.MILLISECONDS.toDays(
Math.abs(expiryCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()));
mais ça ne marche pas pour moi.
vous avez reconnu le problème à la première étape en réglant l'heure à minuit: cela a fait en sorte que toutes les heures, minutes et secondes étaient à zéro. Mais vous n'allez pas assez loin! Vous devez également vous assurer que le millisecondes sont mis à zéro, aussi.
voici un code pour faire exactement cela.
protected long truncate_to_seconds (long date_in_millis) {
long l = date_in_millis / 1000L;
l *= 1000L;
return l;
}
je crois que vous devez tronquer le temps partiel à partir de la date avant de calculer la différence comme suit:
DateFormat formatter= new SimpleDateFormat("MM/dd/yyyy");
String truncatedDateString1 = formatter.format(date1);
Date truncatedDate1 = formatter.parse(truncatedDateString1);
String truncatedDateString2 = formatter.format(date2);
Date truncatedDate2 = formatter.parse(truncatedDateString2);
long timeDifference = truncatedDate2.getTime()- truncatedDate1.getTime();
int daysInBetween = timeDifference / (24*60*60*1000);
espérons que ça marche.
j'ai ma fonction comme but à votre demande. J'utilisais calendar, j'ai mis tout le temps à 0 avant de comparer.
int countDays(Date dateBegin, Date dateEnd) {
if (dateBegin == null || dateEnd == null)
return 0;
Calendar from = asCalendar(dateBegin); // asCalendar() Initialise a Calendar from a Date
Calendar to = asCalendar(dateEnd);
// Set the time part to 0
from.set(Calendar.MILLISECOND, 0);
from.set(Calendar.SECOND, 0);
from.set(Calendar.MINUTE, 0);
from.set(Calendar.HOUR_OF_DAY, 0);
to.set(Calendar.MILLISECOND, 0);
to.set(Calendar.SECOND, 0);
to.set(Calendar.MINUTE, 0);
to.set(Calendar.HOUR_OF_DAY, 0);
int nbJours = 0;
for (Calendar c = from ; c.before(to) ; c.add(Calendar.DATE, +1))
{
nbJours++;
}
for (Calendar c = from ; c.after(to) ; c.add(Calendar.DATE, -1))
{
nbJours--;
}
return nbJours;
}