Comportamiento diferente de la clase Calendar en Java y Android

Estaba trabajando en recuperar la fecha de la primera semana del año y encontré un comportamiento muy extraño.

He probado el siguiente fragmento de código tanto en la aplicación de consola de Java y emulador de Android y produce diferentes resultados.

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

La siguiente producción fue producida

Android log cat: 2012/09/17 (incorrecto)

Consola Java: 2012/01/01 (correcto)

Y lo extraño es que si usé código siguiente en Android y Java produce la misma salida correcta. La única diferencia fue que intercambié la 2ª y 3ª línea del código anterior.

  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 (correcto)

Consola Java: 2012/01/01 (correcto)

Soy muy curioso saber sobre esto.

Gracias por adelantado.

Parece que la clase Calendar tiene internamente dos contenedores de datos.

protected long time

protected int[] fields

Por lo tanto, cuando llama a cal.set(Calendar.WEEK_OF_YEAR, 1) , cambia los valores en los fields , no el time de la clase.

En Java API

Protected abstract void computeFields ()

Convierte el valor de tiempo actual de milisegundos en valores de campo de calendario en campos []. Esto le permite sincronizar los valores del campo de calendario con una nueva hora que se establece para el calendario. El tiempo no es recalculado primero; Para recalcular el tiempo, luego los campos, llame al método complete ().

Creo que en el primer caso de Android computeFields() no se llama internamente.

Para comprobar mi teoría, probé el código siguiente:

 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 [ time = 1348010308802, areFieldsSet = true , lenient = true, zone = org.apache.harmony.luni.internal.util.ZoneInfo ["null", mRawOffset = 0, mUseDst = false], firstDayOfWeek = 1 , DAY_OF_YEAR == 26, DAY_OF_YEAR == 262, DAY_OF_WEEK == 3 , DAY_OF_WEEK_IN_MONTH == 3, DAY_OF_YOUR == 3 , DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, HOUR == 11, HOUR_OF_DAY = 23, MINUTE == 18, SECOND == 28, MILLISECOND == 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.09.18

Java.util.GregorianCalendar [ time = ?, areFieldsSet = false , lenient = true, zone = org.apache.harmony.luni.internal.util.ZoneInfo ["null", mRawOffset = 0, mUseDst = false], firstDayOfWeek = 1 , DAY_OF_YEAR == 26, DAY_OF_YEAR == 262, DAY_OF_WEEK == 3 , DAY_OF_WEEK_IN_MONTH == 3, DAY_OF_MONTH == 3 , DAY_OF_WEK == 3, DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, HOUR == 11, HOUR_OF_DAY = 23, MINUTE == 18, SECOND == 28, MILLISECOND == 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.01.03

Java.util.GregorianCalendar [ time = ?, areFieldsSet = false , lenient = true, zone = org.apache.harmony.luni.internal.util.ZoneInfo ["null", mRawOffset = 0, mUseDst = false], firstDayOfWeek = 1 , DAY_OF_YEAR == 26, DAY_OF_YEAR == 262, DAY_OF_WEEK == 1 , DAY_OF_WEEK_IN_MONTH == 3, DAY_OF_MONTH == 4, DAY_OF_MONTH == 18, DAY_OF_YEAR == 1 , DAY_OF_WEEK_IN_MONTH == 3, AM_PM == 1, HOUR == 11, HOUR_OF_DAY = 23, MINUTE == 18, SECOND == 28, MILLISECOND == 802, ZONE_OFFSET == 0, DST_OFFSET == 0]

2012.09.16

Como podemos ver arriba, los valores en los campos se cambian, pero el tiempo interno se denomina ? , Diciendo que la time no se sincroniza con los fields .

Tienes un time sincronizar usando el método getTime() y lo imprimiste.

Creo que Calendar en Android está diseñado para retrasar la sincronización hasta que sea realmente necesario.

ADICIONAL

He encontrado lo siguiente en Java API:

Los campos del calendario se pueden cambiar usando tres métodos: set (), add (), y roll ().

Set (f, value) cambia el campo f a value. Además, establece una variable de miembro interna para indicar que se ha cambiado el campo f. Aunque el campo f cambia inmediatamente, los milisegundos del calendario no se vuelven a calcular hasta que se realice la siguiente llamada a get (), getTime () o getTimeInMillis () . Por lo tanto, varias llamadas a set () no activan múltiples cálculos innecesarios. Como resultado de cambiar un campo usando set (), otros campos también pueden cambiar, dependiendo del campo, el valor del campo y el sistema de calendario. Además, get (f) no necesariamente devolverá el valor después de que los campos hayan sido recalculados. Las especificaciones son determinadas por la clase concreta del calendario.

ADICIONAL

Para comprobar si "Los detalles están determinados por la clase de calendario concreta" es cierto, comprobé los códigos reales de Dalvik y JDK 6.

Set method in Calendario de Dalvik

Desde 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; } } 

Método Set en el Calendario de 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(); } } 

Las implementaciones específicas son bastante diferentes. Para averiguar la razón exacta de su problema, debe examinar ambas implementaciones en detalle.

  • Compatible con aplicaciones no Ice Cream Sandwich
  • SwipeRefreshLayout activar programaticamente
  • Ocultar el texto en un botón en android?
  • Socket IPv6 en Android
  • Incluyendo Tess4J a un proyecto Java como biblioteca en Eclipse
  • Combinación de java y nodejs para la aplicación android
  • Biblioteca de soporte de diseño de Android: NavigationView código fuente
  • Tools.jar no se encuentra OS X
  • Modelos de fachadas y clases de Managers
  • ¿El Apache DefaultHttpClient (Android) y NSURLConnection (iOS) vuelven a intentar las conexiones fallidas con versiones de protocolo anteriores?
  • Actividades
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.