Calendrier Java – La date est imprévisible après la définition de day_of_week

J’ai le code suivant dans un test JUnit, qui semblait fonctionner la semaine dernière est en train d’échouer cette semaine:

Calendar cal = Calendar.getInstance(); cal.set(2011, Calendar.JULY, 12); cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15 System.out.println(cal.get(Calendar.DATE)); 

Comme vous pouvez probablement le déduire de mon commentaire, puisque le 12 est un mardi, je m’attends à ce que la date soit 15 après le réglage de DAY_OF_WEEK sur vendredi. Toutefois, la valeur imprimée est 22 et provoque l’échec du test.

Si je modifie toutefois le code comme suit et ajoute un appel supplémentaire pour obtenir:

 Calendar cal = Calendar.getInstance(); cal.set(2011, Calendar.JULY, 12); System.out.println(cal.get(Calendar.DATE)); cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15 System.out.println(cal.get(Calendar.DATE)); 

Je reçois le résultat attendu, 12 et 15.

Quelqu’un peut-il expliquer ce qui se passe et pourquoi ce test a fonctionné la semaine dernière?

La première chose à comprendre est que Mois + Jour + JourOuhWeek ne signifie rien pour le calendrier. Le calendrier calculera la valeur réelle de la date en fonction de

ANNÉE + MOIS + DATE

ou

ANNÉE + MOIS + SEMAINE_OF_MONTH + DAY_OF_WEEK

(Ou d’autres combinaisons comme année + jour de l’année, etc.) Donc Date + DayOfWeek ne signifie pas grand chose en soi.

La deuxième chose à comprendre est que lorsque vous définissez un calendrier Java, celui-ci ne recalcule pas l’heure absolue ni ne met à jour les champs associés tant qu’une opération qui force le calcul ne se produit.

Après votre premier ensemble, le calendrier est dans un état conflictuel. Le mois et le jour indiquent que c’est le 12 juillet, mais la «semaine du mois» et le «jour de la semaine» indiquent toujours que c’est aujourd’hui, peu importe ce qui se passe aujourd’hui. Vous appelez ensuite le jour de la semaine au vendredi. Alors maintenant, année, mois et jour disent le 12 juillet, mais les champs “semaine du mois” et “jour de la semaine” indiquent le vendredi de “cette” semaine.

Les règles du calendrier indiquent que le dernier champ défini “gagne” en cas de conflit. La combinaison de la semaine du mois et du jour de la semaine pour indiquer le vendredi de cette semaine sert alors à calculer les autres champs.

L’insertion d’un get in the middle ‘corrige’ le problème, car il oblige tout l’état interne du calendrier à être recalculé au mardi 12 juillet avant de passer à vendredi, de sorte qu’il n’y a pas de conflit interne. La «semaine du mois» a été définie sur la semaine qui contient le 12 juillet par le recalcul effectué avant de définir le jour de la semaine sur vendredi.

Edit: Désolé de faire des changements après deux jours, j’ai remarqué l’ouverture de cet onglet dans un ancien onglet de navigateur et j’ai pensé que je pourrais développer pour aider avec espoir les futurs utilisateurs de Google:

La raison pour laquelle cela a fonctionné pour Jon dans les commentaires est qu’il habite à Londres. Son ordinateur pense que les semaines commencent le lundi. Donc, lorsqu’on lui a demandé vendredi de ‘cette’ semaine, il a tout de même répondu le 15 juillet à la question du dimanche 17 juillet. Je soulève cette question parce que les premiers jours de la semaine dans différentes langues constituent un autre moyen d’essayer d’utiliser les champs WEEK_OF dans un calendrier.

Il y a un bogue 4655637 (qui ressemble à votre problème). J’ai vérifié ce code sous le dernier JDK6 (Windows) et j’en ai 15 dans les deux cas. BTW: Je suggère de toujours utiliser GregorianCalendar classe GregorianCalendar de manière explicite à moins que vous ne vouliez quelque chose d’autre (en fonction de vos parameters régionaux).

EDIT: documents officiels :

Voici les combinaisons par défaut des champs du calendrier. La combinaison la plus récente, déterminée par le dernier champ unique défini, sera utilisée.

Pour les champs de date:

  YEAR + MONTH + DAY_OF_MONTH YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK YEAR + DAY_OF_YEAR YEAR + DAY_OF_WEEK + WEEK_OF_YEAR 

Pour les champs de l’heure du jour:

  HOUR_OF_DAY AM_PM + HOUR 

En plus de la réponse claire de @ Affe, les combinaisons suivantes semblent fonctionner (à partir du lien de rapport de bogue de @ GrzegorzSzpetkowski)

Le calendrier attend les combinaisons suivantes de champs pour déterminer une date.

  MONTH + DAY_OF_MONTH MONTH + WEEK_OF_MONTH + DAY_OF_WEEK MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK DAY_OF_YEAR DAY_OF_WEEK + WEEK_OF_YEAR 

Lorsque vous définissez DAY_OF_WEEK, le calendrier attend un champ hebdomadaire (WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH ou WEEK_OF_YEAR) a également été défini. Évitez donc de définir DAY_OF_WEEK sans définir l’un des champs de la semaine.