Il existe de nombreux pièges auxquels les gens ont tendance à devenir accros s'ils ne savent pas comment implémenter le calcul de la date en Java, et il y a des obstacles autour d'eux, alors trions-les. Par exemple, supposons que vous souhaitiez obtenir la date du dernier jour du mois suivant.
Il est préférable d'utiliser l'API standard, l'API de date et d'heure.
LocalDate nextMonth = LocalDate.now().plusMonths(1L);
LocalDate lastDayOfNextMonth = nextMonth.withDayOfMonth(nextMonth.lengthOfMonth());
Par exemple, si vous faites cela le 31/08/2016, le code de la première ligne ajoutera 1 au mois de .plusMonths (1L)
, mais 9/31 n'existe pas, il est donc valide pour la dernière fois en septembre. Il sera ajusté au jour 9/30. (La deuxième ligne définit également le dernier jour du 30 septembre et lastDayOfNextMonth sera 2016/9/30)
cf.) https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#plusMonths-long-
L'API standard peut être implémentée à l'aide de java.util.Calendar.
Calendar calendar = new GregorianCalendar();
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
Vous pouvez maintenant obtenir la date du dernier jour du mois suivant. Par exemple, si vous faites cela le 31/08/2016, l'ajout de 1 au 31/09 sera ajusté au 30/09, qui est le dernier jour valide de septembre en raison d'une date inexistante. Jusqu'à présent, il semble que la convivialité ne soit pas si différente de l'API Date et heure, mais comparons l'explication dans le document API. (Ajoutez des sauts de ligne pour faciliter la lecture)
Date et heure API LocalDate # plusMonths Depuis https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#plusMonths-long-
Renvoie une copie de ce LocalDate plus le nombre de mois spécifié.
Cette méthode ajoute le montant spécifié au champ du mois en trois étapes.
Ajoute le nombre de mois entré dans le champ mois Vérifiez si la date résultante n'est pas valide Ajustez "Month Day" au dernier jour valide si nécessaire
Par exemple, 2007-03-Si vous ajoutez le 31 janvier 2007-04-Il y aura une date invalide de 31. Au lieu de renvoyer un résultat invalide 2007, le dernier jour valide du mois-04-30 est sélectionné.
Cette instance est immuable et n'est pas affectée par cet appel de méthode. ```
GregorianCalendar#add Depuis http://docs.oracle.com/javase/jp/7/api/java/util/GregorianCalendar.html#add(int,%20int)
Spécifié en fonction des règles du calendrier(Signé)Ajoute la durée au champ de calendrier spécifié.
Ajouter la règle 1. La valeur du champ après avoir soustrait la valeur du champ dans l'appel avant l'appel est le montant du dépassement modulaire survenu dans le champ. Le dépassement fait que la valeur du champ dépasse la plage, ce qui entraîne l'incrémentation ou la décrémentation du champ suivant plus grand. Se produit lorsque la valeur d'un champ est ajustée pour se situer dans cette plage.
Ajouter la règle 2. Si un petit champ est censé être invariant, car la valeur minimale ou maximale a changé depuis que le champ a été modifié. S'il n'est pas égal à son prédécesseur, la valeur du champ est ajustée pour être aussi proche que possible de sa valeur attendue. Petits champs Représente une petite unité de temps. HOUR est DAY_OF_Un champ plus petit que MONTH. Les petits champs qui ne devraient pas être invariants ne sont pas ajustés. Le système de calendrier détermine les champs censés être invariants. ```
Bien que le comportement de LocalDate # plusMonths soit clairement indiqué, je ne suis pas sûr de ce que dit GregorianCalendar # add. .. .. Les règles d'ajout 1 et 2 sont également expliquées au début de http://docs.oracle.com/javase/jp/7/api/java/util/Calendar.html, où elles se présentent comme suit: Certains exemples sont donnés d'une manière un peu plus facile à comprendre, mais je pense que c'est encore difficile à comprendre.
Exemple:Considérons d'abord le calendrier grégorien défini le 31 août 1999. ajouter(Calendar.MONTH, 13)Quand vous appelez
Le calendrier est fixé au 30 septembre 2000. L'ajout de 13 mois à août se traduira par septembre de l'année suivante, alors ajoutez la règle 1
Le champ MONTH est défini sur septembre. JOURNÉE_OF_MOIS ne peut pas être fait le 31 septembre dans le calendrier grégorien, donc par Ajouter la règle 2
DAY_OF_MONTH est défini sur la valeur la plus proche possible de 30. Ceci est un petit champ, mais lorsque le mois change dans GregorianCalendar
JOUR pendant que les changements sont prévus_OF_SEMAINE n'est pas ajustée par la règle 2.
De plus, si le mois du mois suivant a déjà été calculé dans le processus précédent et que vous souhaitez acquérir uniquement le dernier jour, si vous essayez de définir le mois suivant avec Calendar # set, vous pouvez être déçu par le comportement inattendu comme suit.
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH,Le mois prochain);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
La seule différence par rapport à ce qui précède est que la deuxième ligne est définie au lieu d'ajouter. Si vous l'exécutez le 31/07, vous obtiendrez le résultat correct du 31/08, mais si vous l'exécutez le 31/08, vous obtiendrez le résultat du 1/10. En effet, dans le cas de set, contrairement à add, un report se produit lorsque la date n'existe pas. Une autre chose à noter ici est que le résultat est 10/1 au lieu de 10/31. S'il y a report lorsque le mois prochain est fixé, il semble naturel que ce soit le 31/10. Afin de vérifier la valeur au moment de la définition du mois suivant, ajoutez le code (3e ligne) qui renvoie la valeur par getTime après avoir défini comme indiqué ci-dessous.
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH,Le mois prochain);
System.out.println(calendar.getTime());
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
Le résultat passe au 31/10. La classe Calendar contient en interne des champs tels que la date et chaque valeur de champ est recalculée lors de l'exécution de la méthode get, add, getTime, etc., tandis que la méthode set est un champ. Il n'est pas recalculé simplement en définissant la valeur sur, mais il sera recalculé collectivement lorsque get etc. sera exécuté ultérieurement. En d'autres termes, au moment du réglage, il n'est pas encore reflété dans la date et l'heure définies dans le calendrier. Par conséquent, si getTime est inséré au milieu de l'ensemble, le résultat sera différent. Je pense que c'est différent du comportement attendu intuitivement, donc si vous ne comprenez pas bien le comportement, vous pouvez par inadvertance créer un bogue. Le comportement de cette méthode set est également expliqué au début du document API http://docs.oracle.com/javase/jp/7/api/java/util/Calendar.html. (Ajoutez des sauts de ligne pour faciliter la lecture)
set(f, value)Change le champ de calendrier f en valeur. De plus, pour indiquer que le champ de calendrier f a changé
Les variables de membre internes sont définies. Le champ de calendrier f change immédiatement, mais la valeur de l'heure du calendrier(milliseconde)Est
get()、getTime()、getTimeInMillis()、add(), Ou rouler()Ne sera pas recalculé avant le prochain appel.
Comme ça, ensemble()Appeler plusieurs fois n'entraîne pas de calculs inutiles. ensemble()Si vous modifiez le champ du calendrier à l'aide de
Les autres champs sont susceptibles d'être modifiés en fonction du champ de calendrier, de la valeur du champ de calendrier et du système de calendrier.
De plus, obtenez(f)Ne renvoie pas toujours la valeur définie en appelant la méthode set après avoir recalculé le champ de calendrier.
Ces détails sont déterminés par la classe de calendrier concrète.
Exemple:Considérons d'abord le calendrier grégorien défini le 31 août 1999. ensemble(Calendar.MONTH, Calendar.SEPTEMBER)Quand vous appelez
La date est fixée au 31 septembre 1999. Ceci est une représentation interne temporaire, getTime()Ce sera le 1er octobre 1999.
Cependant, getTime()Avant d'appeler le poste(Calendar.DAY_OF_MONTH, 30)Lorsque vous appelez set()Parce que le recalcul se fait après lui-même
La date est fixée au 30 septembre 1999.
Dans le cas de l'API Java 8 Date and Time,
LocalDate nextMonth = LocalDate.now().withMonth(Le mois prochain);
LocalDate lastDayOfNextMonth = nextMonth.withDayOfMonth(nextMonth.lengthOfMonth());
Même si le code qui spécifie le mois suivant est exécuté le 31/08 comme dans le cas d'ajout, le résultat sera le 30/09. Le document API https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#withMonth-int- a également la description suivante, vous pouvez donc être rassuré.
Si ce "jour du mois" n'est pas valide pour cette année, il sera remplacé par le dernier jour valide du mois.
En outre, je prévois de définir septembre comme une erreur que les débutants ont tendance à faire, ce qui n'est pas intuitif pour Calendar.
calendar.set(Calendar.MONTH, 9);
Il y a aussi un cas où il est écrit. En fait, cela signifie que octobre a été fixé. Le mois du calendrier est 0-11, pas 1-12, donc si vous souhaitez spécifier septembre
calendar.set(Calendar.MONTH, 8);
Doit être écrit. C'est difficile à lire et à comprendre intuitivement, donc
calendar.set(Calendar.MONTH, Calendar.SEPTEMBER);
Il est préférable d'écrire en utilisant des constantes comme.
Si vous avez une bonne compréhension du comportement de la classe Calendar comme décrit ci-dessus, vous pouvez utiliser Calendar, mais c'est compliqué. En utilisant Joda-Time http://www.joda.org/joda-time/, qui était un élément essentiel des bibliothèques de dates en Java avant Java 7, vous pouvez écrire simplement comme suit.
Date lastDayOfNextMonth = LocalDate.now().plusMonths(1).dayOfMonth().withMaximumValue().toDate();
Pour préciser le mois du mois suivant, ce sera comme suit.
Date lastDayOfNextMonth = LocalDate.now().withMonthOfYear(Le mois prochain).dayOfMonth().withMaximumValue().toDate();
Si vous n'utilisez pas Joda-Time et envisagez de migrer vers Java 8 à l'avenir, faites-en un développeur Joda-Time et lisez les spécifications de l'API Date and Time (JSR-310). Stephen Colebourne a publié ThreeTen-Backport http://www.threeten.org/threetenbp/ en tant que bibliothèque qui rétroporte les classes API Java8 Date et Heure pour Java6 / 7, alors utilisez-la. Je pense que c'est aussi bien.
Recommended Posts