еще одно странное поведение с GregorianCalendar - PullRequest
3 голосов
/ 15 июля 2010

Посмотрите на фрагмент кода ниже:

Calendar today1 = Calendar.getInstance();
today1.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today1.getTime());

Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());

Я в замешательстве ... Предполагая, что я запускаю его сегодня, как 14 июля 2010 года, вывод:

Fri Jul 16 14:23:23 PDT 2010
Wed Jul 14 00:00:00 PDT 2010

Самое неприятное, что если я добавлю today2.getTimeInMillis () (или любой другой метод get ()), это даст согласованный результат. Для кода ниже:

Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.getTimeInMillis();
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());

Результат:

Fri Jul 16 00:00:00 PDT 2010

Ответы [ 3 ]

4 голосов
/ 15 июля 2010

Ответ фактически задокументирован в JavaDoc для java.util.Calendar

Цитируется здесь:

set (f, value) меняет поле календаря f на значение. Кроме того, он устанавливает внутреннюю переменную-член, чтобы указать, что поле календаря f было изменено. Хотя поле f изменено немедленно, календарь миллисекунды не пересчитывается до следующий вызов get (), getTime () или getTimeInMillis () выполнен.

Итак, это объясняет поведение, которое вы видите, но я согласен с другим ответчиком на ваш вопрос, который вы должны рассмотреть JodaTime , если вы собираетесь делать много датирования.

2 голосов
/ 15 июля 2010

На самом деле вы должны использовать Calendar#getInstance() для получения экземпляра, а не new GregorianCalendar().Замените эту строку на

Calendar today2 = Calendar.getInstance();
today2.set(2010, Calendar.JULY, 14);

, и она пойдет хорошо.

Извините, нет подробного объяснения поведения, ожидайте, что Calendar наряду с java.util.Date являются одним из главных эпосов.сбои в текущем API Java SE.Если вы выполняете интенсивные операции с датой / временем, то я рекомендую взглянуть на JodaTime .Предстоящий новый Java 7 будет поставляться с улучшенным API даты / времени на основе JodaTime ( JSR-310 ).

0 голосов
/ 15 июля 2010

(Извините за редактирование, я хотел, чтобы это было немного более читабельным, но не мог сделать это правильно, когда я первоначально написал ответ ... теперь это длина эссе, но вы идете ...)

Просто, чтобы добавить к тому, что уже было сказано, проблема возникает из-за того, что возвращенные Calendar экземпляры готовятся по-другому.Лично я чувствую, что это недостаток дизайна, но для этого может быть веская причина.

Когда вы вызываете Calendar.getInstance(), он создает новый GregorianCalendar с использованием конструктора по умолчанию.Этот конструктор вызывает setCurrentTimeMillis(time) с текущим системным временем, а затем вызывает защищенный метод complete().

Однако, когда вы создаете новый GregorianCalendar с использованием созданного вами конструктора, complete() никогда не будетназывается;вместо этого, среди прочего, только set(field, value) вызывается для различных предоставляемых битов информации.Это все хорошо, но имеет некоторые запутанные последствия.

Когда в первом случае вызывается complete(), проверяются переменные-члены, на которые ссылается пылесос, чтобы определить, какую информацию следует пересчитать.В результате получается ветвь, которая заставляет вычислять все поля (DAY, WEEK_OF_MONTH и т. Д.).Обратите внимание, что Calendar действительно ленив;просто случается так, что использование этого метода создания экземпляров приводит к явному пересчету (или, в данном случае, первоначальному вычислению) на месте.

Итак, какое влияние это оказывает?Учитывая, что в случае создания второго объекта не было начального вычисления поля, два объекта имеют совершенно разные состояния.Первый содержит всю информацию о своих полях, а второй содержит только ту информацию, которую вы предоставили.Когда вы вызываете различные методы get*(), это не должно иметь значения, потому что любые изменения должны провоцировать ленивый пересчет при извлечении информации.Однако порядок, в котором происходит этот пересчет, показывает различия между двумя различными начальными состояниями.

В вашем конкретном случае это связано со следующим соответствующим кодом в computeTime(), который обязательно вызывается для вычисленияправильное время, когда вы запрашиваете его с помощью getTime():

boolean weekMonthSet = isSet[WEEK_OF_MONTH] || isSet[DAY_OF_WEEK_IN_MONTH];
...
boolean useDate = isSet[DATE];

if (useDate && (lastDateFieldSet == DAY_OF_WEEK
      || lastDateFieldSet == WEEK_OF_MONTH
      || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
    useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
}

В первом случае все поля устанавливаются в соответствии с этим начальным вычислением.Это позволяет weekMonthSet быть истинным, что, наряду с DAY_OF_WEEK, которое вы указали при вызове для set(field, value), приводит к тому, что useDate будет ложным.

Однако во втором случае, поскольку никакие поля не были рассчитаны, единственные установленные поля - это те, которые вы указали в конструкторе и при последующем вызове set(field, value).Таким образом, useDate останется истинным, потому что isSet[DATE] является истинным для вашего конструктора, но weekMonthSet является ложным, поскольку другие поля в объекте нигде не были вычислены и не установлены вами.

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

Наконец, возникает вопрос о том, зачем звонить getTimeInMillis() перед вызовомgetTime() исправит неожиданное поведение.Как оказалось, поля будут пересчитаны в результате вашего вызова set(field, value) в обоих объектах.Просто так происходит после времени, по какой-то (вероятно, подлинной) причине.Следовательно, принудительное вычисление времени один раз за секунду Calendar по существу выровняет состояния двух объектов.После этого я считаю, что вызовы get*() должны все работать согласованно для обоих объектов.

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

Итак, вкратце, как уже упоминалось, JodaTime ваш други, очевидно, эти классы менее.:)

...