Pregunta

Me encontré con un comportamiento extraño que me ha dejado curioso y sin una explicación satisfactoria hasta el momento.

Para simplificar, he reducido los síntomas que he notado con el siguiente código:

import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;

public class CalendarTest {
    public static void main(String[] args) {
        System.out.println(new SimpleDateFormat().getCalendar());
        System.out.println(new GregorianCalendar());
    }
}

Al ejecutar este código, consigo algo muy similar a la siguiente salida:

java.util.GregorianCalendar[time=-1274641455755,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=1929,MONTH=7,WEEK_OF_YEAR=32,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=222,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=8,HOUR_OF_DAY=20,MINUTE=55,SECOND=44,MILLISECOND=245,ZONE_OFFSET=-28800000,DST_OFFSET=0]
java.util.GregorianCalendar[time=1249962944248,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2009,MONTH=7,WEEK_OF_YEAR=33,WEEK_OF_MONTH=3,DAY_OF_MONTH=10,DAY_OF_YEAR=222,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=8,HOUR_OF_DAY=20,MINUTE=55,SECOND=44,MILLISECOND=248,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

(Lo mismo ocurre si doy una cadena de formato válido como "yyyy-MM-dd" a SimpleDateFormat.)

Perdona las líneas no envolver horrendos, pero es la forma más fácil de comparar los dos. Si se desplaza a unos 2 / 3rds de la manera más, podrás ver que los calendarios tienen valores de año de 1929 y 2009, respectivamente. (Hay algunas otras diferencias, como la semana del año, día de la semana, y la diferencia de DST.) Ambos son, obviamente, las instancias de GregorianCalendar, pero la razón por la que difieren son desconcertantes.

Por lo que puedo decir al formateador produce precisa cuando el formato de fechas objetos que se le pasan. Obviamente, la funcionalidad correcta es más importante que el año de referencia correcta, pero la discrepancia es desconcertante, no obstante. No creo que tendría que establecer el calendario en una fecha formateador de nuevo sólo para el año en curso ...

He probado esto en Macs con Java 5 (OS X 10.4, PowerPC) y Java 6 (OS X 10.6, Intel) con los mismos resultados. Dado que esta es una API de biblioteca de Java, que supongo que se comporta igual en todas las plataformas. Cualquier idea sobre lo que está en curso de realización aquí?

(Nota:. esta pregunta SO es algo relacionado, pero no es lo mismo)


Editar

Las respuestas a continuación todos ayudaron a explicar este comportamiento. Resulta que los Javadocs para SimpleDateFormat realidad documentar esto hasta cierto punto:

  

"Para analizar con el patrón años abreviada (" Y" o 'aa'), SimpleDateFormat debe interpretar el año abreviada en relación con algún siglo. Lo hace mediante el ajuste de las fechas para estar dentro de 80 años antes y 20 años después de la hora se crea la instancia SimpleDateFormat ".

Así que, en lugar de obtener de fantasía con el año de la fecha en que se está analizando, que acaba de establecer el calendario interno de vuelta 80 años de forma predeterminada. Esa parte no está documentada en sí, pero cuando se sabe de ello, las piezas encajan entre sí todos.

¿Fue útil?

Solución

No estoy seguro de por qué Tom dice que "es algo que ver con la serialización", pero tiene la línea derecha:

private void initializeDefaultCentury() {
    calendar.setTime( new Date() );
    calendar.add( Calendar.YEAR, -80 );
    parseAmbiguousDatesAsAfter(calendar.getTime());
}

Es la línea 813 en SimpleDateFormat.java, que es muy tarde en el proceso. Hasta ese momento, el año es correcta (como en el resto de la parte de fecha), entonces se reduce en 80.

Aha!

La llamada a parseAmbiguousDatesAsAfter() es la misma función privada que set2DigitYearStart() llamadas:

/* Define one-century window into which to disambiguate dates using
 * two-digit years.
 */
private void parseAmbiguousDatesAsAfter(Date startDate) {
    defaultCenturyStart = startDate;
    calendar.setTime(startDate);
    defaultCenturyStartYear = calendar.get(Calendar.YEAR);
}

/**
 * Sets the 100-year period 2-digit years will be interpreted as being in
 * to begin on the date the user specifies.
 *
 * @param startDate During parsing, two digit years will be placed in the range
 * <code>startDate</code> to <code>startDate + 100 years</code>.
 * @see #get2DigitYearStart
 * @since 1.2
 */
public void set2DigitYearStart(Date startDate) {
    parseAmbiguousDatesAsAfter(startDate);
}

Ahora veo lo que está pasando. Pedro, en su comentario sobre "manzanas y naranjas", tenía razón! El año en SimpleDateFormat es el primer año de la "siglo por defecto", el rango en el que (por ejemplo, "01/12/14") se interpreta una cadena años de dos dígitos que sea. Ver http: / /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

Así pues, en un triunfo de la "eficiencia" sobre la claridad, el año en el SimpleDateFormat se utiliza para almacenar "el inicio del período de 100 años en el que se analizan años de dos dígitos", no el año actual!

Gracias, esto fue muy divertido - y finalmente me llevó a instalar el código fuente del JDK (. Sólo tengo espacio total de 4 GB en mi partición /)

Otros consejos

Se está investigando el comportamiento interno. Si esto va fuera de la API publicada entonces usted está viendo cosas indefinido, y no debe preocuparse por ello.

Aparte de eso, me creer que el año 1929 se utiliza para considerar cuando de interpretar un año de dos dígitos como en el 19xx 20xx en lugar de la.

SimpleDateFormat tiene estado interno mutable. Esta es la razón por lo evito como la peste (recomiendo Joda Time ). Este calendario interno se utiliza probablemente durante el proceso de análisis de una fecha, pero no hay razón por la que se inicializa a nada en particular antes de que haya analizado una fecha.

Aquí hay algo de código para ilustrar:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;

public class DateTest {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        System.out.println("sdf cal: " + simpleDateFormat.getCalendar());
        System.out.println("new cal: " + new GregorianCalendar());
        System.out.println("new date: " + simpleDateFormat.format(new Date()));
        System.out.println("sdf cal: " + simpleDateFormat.getCalendar());
    }
}

Mirando a través de SimpleDateFormat parece como si fuera algo que ver con la serialización:

/* Initialize the fields we use to disambiguate ambiguous years. Separate
 * so we can call it from readObject().
 */
private void initializeDefaultCentury() {
    calendar.setTime( new Date() );
    calendar.add( Calendar.YEAR, -80 );
    parseAmbiguousDatesAsAfter(calendar.getTime());
}
System.out.println(new SimpleDateFormat().getCalendar());
System.out.println(new GregorianCalendar());

comparando encima de código está comparando manzanas y peras

La primera te ofrece una herramienta para analizar la cadena en fechas y viceversa El segundo es un DateUtility que le permite manipular fechas

En realidad no hay una razón por la que debe proporcionar una salida similar.

Comparar con la siguiente

System.out.println(new String() );
System.out.println(new Date().toString() );

Ambas líneas de salida de una cadena, pero Logicly no podrías esperar el mismo resultado

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top