Ответ 1
Поскольку GregorianCalender расширяет класс Calendar, он наследует все его функции. Из Java Doc
set(f, value) changes calendar field f to value. In addition, it sets an internal
member variable to indicate that calendar field f has been changed. Although
calendar field f is changed immediately, the calendar time value in
milliseconds is not recomputed until the next call to get(), getTime(),
getTimeInMillis(),add(), or roll() is made. Thus, multiple calls to set() do not
trigger multiple, unnecessary computations. As a result of changing a calendar
field using set(), other calendar fields may also change, depending on the calendar
field, the calendar field value, and the calendar system. In addition, get(f) will
not necessarily return value set by the call to the set method after the calendar
fields have been recomputed.
Пример Java Doc:
Consider a GregorianCalendar originally set to August 31, 1999. Calling
set(Calendar.MONTH, Calendar.SEPTEMBER) sets the date to September 31, 1999. This
is a temporary internal representation that resolves to October 1, 1999 if
getTime()is then called. However, a call to set(Calendar.DAY_OF_MONTH, 30) before
the call to getTime() sets the date to September 30, 1999, since no recomputation
occurs after set() itself.
Также класс Календарь имеет следующий побочный эффект: -
In lenient mode, all of the Calendar fields are normalized.
Это означает, что когда вы вызываете setTimeZone(
) и set(Calendar.HOUR_OF_DAY, 0)
, он устанавливает внутреннюю переменную-член, чтобы указать, что установлены поля Календаря. Но Календарное время не пересчитывается в это время. Время календаря пересчитывается только после вызова get()
, getTime()
, getTimeInMillis()
, add()
или roll()
.
Это ошибка в классе календаря JDK-4827490: (cal) Doc: поведение Calendar.setTimeZone недокументировано
Теперь ваш пример изменен, чтобы получить работу, как показано ниже: -
public class DateTimeTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
GregorianCalendar date = (GregorianCalendar) GregorianCalendar.getInstance(TimeZone
.getTimeZone("CET"));
// 26.10.2014 01:00:00
date.set(2014, 9, 26, 1, 0, 0);
// 25 Oct 2014 23:00:00 GMT 1414278000764
System.out.println("CET to UTC : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
date.setTimeZone(TimeZone.getTimeZone("UTC"));
// date.roll(Calendar.HOUR_OF_DAY, true); //uncomment this line & comment below line & check the different behavior of Calender.
date.get(Calendar.HOUR_OF_DAY);
// 25 Oct 2014 23:00:00 GMT 1414278000764
System.out.println("UTC : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
date.set(Calendar.HOUR_OF_DAY, 0);
// 25 Oct 2014 00:00:00 GMT 1414195200218
System.out.println("UTC Midnight : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
}
}
Выход:
CET to UTC : 25 Oct 2014 23:00:00 GMT 1414278000008
UTC : 25 Oct 2014 23:00:00 GMT 1414278000008
UTC Midnight : 25 Oct 2014 00:00:00 GMT 1414195200008
Надеюсь, теперь вы получите четкое представление о непредсказуемом поведении класса Calendar.