Un comportement étrange de java.util.Calendrier des événements sur février


J'ai fait face à un comportement étrange de java.util.Calendrier:

import static org.junit.Assert.*;
import org.junit.Test;

import java.util.Calendar;

public class Tester1 {
    @Test
    public void test_monthOfDate() {
        assertEquals(1, monthOfDate(2013, 1, 30)); // OK
        assertEquals(1, monthOfDate(2013, 1, 31)); // OK

        // Start of February
        assertEquals(2, monthOfDate(2013, 2, 1));  // FAIL
        assertEquals(2, monthOfDate(2013, 2, 28)); // FAIL
        // to the end of it

        // and after that it is okay also
        assertEquals(3, monthOfDate(2013, 3, 1));  // OK
    }

    public int monthOfDate(int year, int month, int day) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);

        // just a simple get! but seems it is very important
        cal.get(Calendar.MONTH);
        //

        cal.set(Calendar.DAY_OF_MONTH, day);

        return cal.get(Calendar.MONTH) + 1;
    }
}

Je veux savoir pourquoi exactement cela se produit?

Author: Ebrahim Byagowi, 2013-01-30

1 answers

Le problème est que vous commencez avec un calendrier le 30 janvier 2013.

Vous définissez alors l'année sur 2013 - ce n'est pas un problème.

Vous définissez ensuite le mois sur 1 (c'est-à-dire février). Qu'espérez-vous arrive ici? Ce qui se passe réellement, c'est qu'il se souviendra qu'il doit définir le mois sur 1, mais pas recalculer la valeur de temps réelle. La valeur de temps sera recalculée lors de votre appel à get cependant, selon le documentation (emphsis le mien):

Set(f, value) change le champ de calendrier f en value. En outre, il définit une variable membre interne pour indiquer que le champ de calendrier f a été modifié. Bien que le champ de calendrier f soit modifié immédiatement, la valeur temporelle du calendrier en millisecondes n'est pas recalculée jusqu'à ce que le prochain appel à get (), getTime (), getTimeInMillis (), add () ou roll() soit effectué. Ainsi, plusieurs appels à set () ne déclenchent pas plusieurs calculs inutiles. En tant que résultat de la modification d'un champ calendrier à l'aide de set(), calendrier autres champs peuvent également changer en fonction du champ calendrier, le calendrier de valeur de champ, et le système de calendrier. De plus, get(f) ne retournera pas nécessairement la valeur définie par l'appel à la méthode set après le recalcul des champs de calendrier. Les spécificités sont déterminées par la classe de calendrier concret.

Lorsque vous essayez de changer "30 janvier" en "30 février" et de forcer un calcul, quoi en fait, se produit, c'est que vous vous retrouvez le 2 mars sur ma boîte - mais cela peut différer sur votre implémentation.

Les meilleurs correctifs sont:

 15
Author: Jon Skeet, 2013-01-30 13:43:26