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

Различное поведение класса Calendar в Java и Android

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

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

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.WEEK_OF_YEAR, 1);
    cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
    System.out.println(sdf.format(cal.getTime()));

Произведен результат:

Android log cat: 2012/09/17 (неверно)

Java-консоль: 2012/01/01 (правильно)

И странно, если бы я использовал следующий код, как в Android, так и в Java, он производит одинаковый корректный вывод. Единственное различие заключалось в том, что я поменял 2-ю и 3-ю строки по указанному выше коду.

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
    cal.set(Calendar.WEEK_OF_YEAR, 1);
    System.out.println(sdf.format(cal.getTime()));

Android log cat: 2012/01/01 (правильный)

Java-консоль: 2012/01/01 (правильно)

Мне очень любопытно узнать об этом.

Спасибо заранее.

4b9b3361

Ответ 1

Кажется, что в классе Calendar есть два контейнера данных.

protected long time

protected int[] fields

Поэтому, когда вы вызываете cal.set(Calendar.WEEK_OF_YEAR, 1), вы изменяете значения в fields, а не time этого класса.

В API Java

protected abstract void computeFields()

Преобразует текущее значение времени миллисекундного времени в значения полей календаря в полях []. Это позволяет синхронизировать значения поля календаря с новым временем, установленным для календаря. Время не пересчитывается сначала; чтобы перекомпилировать время, затем поля, вызовите метод complete().

Я думаю, что в первом случае Android computeFields() не называется внутренне.

Чтобы проверить мою теорию, я проверил следующий код:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
Calendar cal = Calendar.getInstance();
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));
cal.set(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));

LogCat:

java.util.GregorianCalendar [ время = 1348010308802, areFieldsSet = истина, мягок = верно, зона = org.apache.harmony.luni.internal.util.ZoneInfo [ "нулевой" , mRawOffset = 0, mUseDst = ложь], firstDayOfWeek = 1, minimalDaysInFirstWeek = 4, ERA == 1, == Год 2012, Месяц == 8, WEEK_OF_YEAR == 38, WEEK_OF_MONTH == 4, DAY_OF_MONTH == 18, day_of_year == 262, DAY_OF_WEEK == 3, DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, ЧАС == 11, HOUR_OF_DAY = 23, МИНУТНЫЙ == 18, ВТОРОЙ == 28, миллисекунды = = 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.09.18

java.util.GregorianCalendar [ время =?, areFieldsSet = ложь, мягок = верно, зона = org.apache.harmony.luni.internal.util.ZoneInfo [ "нулевой" , mRawOffset = 0, mUseDst = ложь], firstDayOfWeek = 1, minimalDaysInFirstWeek = 4, ERA == 1, == Год 2012, Месяц == 8, WEEK_OF_YEAR == 1, WEEK_OF_MONTH == 4, DAY_OF_MONTH == 18, day_of_year == 262, DAY_OF_WEEK == 3, DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, ЧАС == 11, HOUR_OF_DAY = 23, МИНУТНЫЙ == 18, ВТОРОЙ == 28, миллисекунды = = 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.01.03

java.util.GregorianCalendar [ время =?, areFieldsSet = ложь, мягок = верно, зона = org.apache.harmony.luni.internal.util.ZoneInfo [ "нулевой" , mRawOffset = 0, mUseDst = ложь], firstDayOfWeek = 1, minimalDaysInFirstWeek = 4, ERA == 1, == Год 2012, Месяц == 8, WEEK_OF_YEAR == 1, WEEK_OF_MONTH == 4, DAY_OF_MONTH == 18, day_of_year == 262, DAY_OF_WEEK == 1, DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, ЧАС == 11, HOUR_OF_DAY = 23, МИНУТНЫЙ == 18, ВТОРОЙ == 28, миллисекунды = = 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.09.16

Как мы видим выше, значения в полях изменяются, но внутреннее время обозначается как ?, говоря, что time не синхронизируется с fields.

Вы получили несинхронизированный time с помощью метода getTime() и распечатали его.

Я думаю, что Календарь в Android предназначен для задержки синхронизации, пока это действительно не понадобится.

ADDED

Я нашел следующее в Java API:

Календарные поля можно изменить тремя способами: set(), add() и roll().

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

ADDED

Чтобы проверить, является ли спецификацией конкретного класса календаря, я проверил действительные коды Dalvik и JDK 6.

Установить метод в календаре Dalvik

из https://www.codeaurora.org/git/projects/qrd-gb-dsds-7225/repository/revisions/cc99b832a941dc8cbb86f1607d04eb87935ddbfd/entry/android/dalvik/libcore/luni/src/main/java/java/util/Calendar.java

public void set(int field, int value) {
    fields[field] = value;
    isSet[field] = true;
    areFieldsSet = isTimeSet = false;
    if (field > MONTH && field < AM_PM) {
        lastDateFieldSet = field;
    }
    if (field == HOUR || field == HOUR_OF_DAY) {
        lastTimeFieldSet = field;
    }
    if (field == AM_PM) {
        lastTimeFieldSet = HOUR;
    }
}

Установить метод в календаре JDK 6

public void set(int field, int value) {
    if (isLenient() && areFieldsSet && !areAllFieldsSet) {
        computeFields();
    }
    internalSet(field, value);
    isTimeSet = false;
    areFieldsSet = false;
    isSet[field] = true;
    stamp[field] = nextStamp++;
    if (nextStamp == Integer.MAX_VALUE) {
        adjustStamp();
    }
}

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