Месяц отключен на один (не из-за того, что он основан на 0)

Когда я устанавливаю месяц на дату, представляющую 1/1/1970, а затем сразу возвращаю месяц назад, он отключается на один.

import java.util.Date;

@Test
public void monthShouldBeExpectedValue() {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new Date(0));

    int expectedMonth = Calendar.JUNE;
    calendar.set(Calendar.MONTH, expectedMonth);
    int actualMonth = calendar.get(Calendar.MONTH);

    assertThat(actualMonth, equalTo(expectedMonth)); // test fails: expected 5 got 6
}

Если я изменю эту строку

calendar.setTime(new Date(0));

к

calendar.setTime(new Date());  // use 'today' instead of 1/1/1970

то тест проходит. Кто-нибудь знает, почему?

Edit

Печатная версия дат:

new Date(0):         Wed Dec 31 19:00:00 EST 1969
date from calendar:  Tue Jul 01 19:00:00 EDT 1969

Я запускаю старый JDK: 1.6.0_30-b12 (64 бит)

Я в восточном стандартном времени.

Ответ 1

Мое предположение заключается в том, что из-за вашего текущего часового пояса время интерпретируется как 31 декабря 1969 года + куча часов. Таким образом, месяц установки к июню должен был произойти 31 июня 1969 года (которого не существует, в июне - 30 дней). Поэтому он перейдет к июлю.

Ответ 2

Прежде всего, напечатайте календарь, чтобы иметь точную дату.

Тогда обычные вопросы:

Какой ваш часовой пояс?

Есть ли у вас летняя экономия времени?

Сменился ли часовой пояс с 1970 года?

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

Ответ 3

Ответ Smallhacker верен.

Joda времени

Для удовольствия я попробовал аналогичный код в Joda-Time 2.3.

Такое же поведение имеет смысл. Если вы попросите DateTime, построенный с миллисекунды-от-эпохи нуля, вы начинаете с 1 января 1970 года. Но это дата-время в UTC (без смещения часового пояса).

Если вы вызываете toString() в этом объекте DateTime с использованием пользовательского часового пояса пользователя, конечно, вы видите другое значение, отличное от Epoch (начало дня 1 января 1970 года). Если пользователь исландский, где они используют UTC круглый год.

Пример кода

DateTime epoch = new DateTime( 0 );
System.out.println( "epoch: " + zero );
System.out.println( "epoch in UTC: " + zero.toDateTime( DateTimeZone.UTC ) );

При запуске в часовом поясе по умолчанию на западном побережье США...

epoch: 1969-12-31T16:00:00.000-08:00
epoch in UTC: 1970-01-01T00:00:00.000Z

Мораль истории

Всегда указывать часовой пояс; никогда не предполагайте.

Работа с значениями даты и времени без явных известных часовых поясов подобна работе над текстовыми файлами без явной кодировки символов. Не умный.

Июнь

Чтобы ответить на ваш вопрос, но используя Joda-Time в одной строке кода...

System.out.println( "June after Epoch: " + new DateTime( 0 ).toDateTime( DateTimeZone.UTC ).monthOfYear().setCopy( DateTimeConstants.JUNE ) );

При запуске...

June after Epoch: 1970-06-01T00:00:00.000Z

Этот же код достаточно умен, чтобы обрабатывать конец месяца. Если оригинал был 31 января, результатом будет 30 июня.