Подтвердить что ты не робот

Календарь Java - дата непредсказуема после установки day_of_week

У меня есть следующий код в тесте JUnit, который, казалось, работал на прошлой неделе, не работает на этой неделе:

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));

Как вы, вероятно, можете сделать вывод из моего комментария, поскольку 12-й вторник, я ожидаю, что Date будет 15 после установки DAY_OF_WEEK в пятницу. Однако напечатанное значение равно 22 и приводит к сбою теста.

Если я, однако, изменим код следующим образом и добавлю дополнительный вызов, чтобы получить:

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));

Я получаю результат, который я ожидаю, 12 и 15.

Может кто-нибудь объяснить, что происходит, и почему этот тест работал на прошлой неделе?

4b9b3361

Ответ 1

Первое, что нужно понять, это то, что Month + Day + DayOfWeek ничего не значит для Календаря. Календарь рассчитает истинное значение даты на основе

ГОД + МЕСЯЦ + ДАТА

или

ГОД + МЕСЯЦ + WEEK_OF_MONTH + DAY_OF_WEEK

(Или некоторые другие комбо, такие как год + день года и т.д.). Таким образом, Date + DayOfWeek не имеет для него особого значения.

Второе, что нужно понять, - это когда вы устанавливаете Java-календарь, он фактически не пересчитывает абсолютное время или связанные с обновлением поля, пока не произойдет операция, которая заставляет вычислять.

После вашего первого набора календарь находится в конфликтном состоянии. Месяц и день говорят, что это 12 июля, но "неделя месяца" и "день недели" все еще говорят, что сегодня, независимо от сегодняшнего дня. Затем вы вызываете установленный день недели в пятницу. Так что теперь год месяц и день говорят 12 июля, но поля "неделя месяца" и "день недели" говорят о пятнице недели 'this'.

Правила календаря говорят о том, что последнее заданное поле "выигрывает", когда есть конфликт, поэтому неделя месяца и дня недели, сочетающаяся с выражением в пятницу на этой неделе, используется для вычисления других полей.

Вставка get в середину "исправляет" его, потому что он заставляет все внутреннее состояние календаря перераспределяться во вторник 12 июля до установки в пятницу, поэтому внутренних конфликтов нет. "Неделя месяца" была настроена на неделю, которая содержит 12 июля путем пересчета до того, как вы установили день недели в пятницу.

Изменить: извините за внесение изменений через два дня, заметив, что это открыто на старой вкладке браузера, и я подумал, что я буду расширять для надежной помощи будущих гуглеров:

Причина, по которой она работала для Джона в комментариях, заключается в том, что он живет в Лондоне. Его компьютер думает, что недели начинаются по понедельникам. Поэтому, когда его спросили в пятницу неделю 'this', он все еще ответил 15 июля, когда его спросили в воскресенье, 17 июля. Я рассказываю об этом, потому что отличные первые дни недели в разных локалях - это еще один способ, который пытается использовать поля WEEK_OF в календаре, сходится.

Ответ 2

Существует Ошибка 4655637 (похожа на вашу проблему). Я проверил этот код под последним JDK6 (Windows), и у меня есть 15 в обоих случаях. BTW: Я предлагаю всегда использовать класс GregorianCalendar, если вы не хотите что-то другое (в зависимости от вашей локали).

Ответ 3

EDIT: официальные документы:

Ниже приведены комбинации полей календаря по умолчанию. самая последняя комбинация, определяемая самым последним установленным одиночным поле.

Для полей даты:

 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

Для полей времени дня:

 HOUR_OF_DAY
 AM_PM + HOUR

В дополнение к четкому ответу @Affe следующие комбинации , похоже, работают (по ссылке на сообщение об ошибке @GrzegorzSzpetkowski)

Календарь ожидает следующих комбинаций полей для определения дата.

 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

Когда вы устанавливаете DAY_OF_WEEK, календарь ожидает поле недели (WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH или WEEK_OF_YEAR) также задавать. Поэтому не устанавливайте DAY_OF_WEEK без установки одной из недель поля.