Pergunta

deparei-me com um comportamento estranho que me deixou curioso e sem uma explicação satisfatória até agora.

Para simplificar, eu já reduziu os sintomas que eu observei com o seguinte 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());
    }
}

Quando eu executar esse código, eu recebo algo muito parecido com o seguinte resultado:

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]

(A mesma coisa acontece se eu fornecer uma seqüência de formato válido como "yyyy-MM-dd" para SimpleDateFormat.)

Perdoe as linhas não envolventes horrendos, mas é a maneira mais fácil de comparar os dois. Se você rolar para cerca de 2 / 3rds do caminho, você verá que os calendários têm valores ano de 1929 e 2009, respectivamente. (Existem algumas outras diferenças, como a semana do ano, dia da semana, e DST offset.) Ambos são, obviamente, casos de GregorianCalendar, mas a razão pela qual eles diferem é intrigante.

De que eu posso dizer o formatador produz precisa ao formatar objetos Date passados ??para ele. Obviamente, a funcionalidade correta é mais importante do que o ano de referência correta, mas a discrepância é desconcertante, no entanto. Eu não acho que eu teria que definir o calendário em um formatador de data marca nova apenas para obter o ano em curso ...

Eu testei isso em Macs com Java 5 (OS X 10.4, PowerPC) e Java 6 (OS X 10.6, Intel) com os mesmos resultados. Uma vez que esta é uma biblioteca Java API, presumo que se comporta da mesma em todas as plataformas. Qualquer visão sobre o que está acontecendo aqui?

. (Nota: Esta questão SO é algo relacionado, mas não o mesmo)


Editar:

As respostas abaixo tudo ajudou a explicar esse comportamento. Acontece que os Javadocs para SimpleDateFormat realmente documentar isso até certo ponto:

"Para analisar com o padrão de ano abreviado (" y" ou 'aa'), SimpleDateFormat deve interpretar o ano em relação abreviado para alguns século. Ele faz isso por datas ajustando a ser dentro de 80 anos antes, e 20 anos após a hora a instância SimpleDateFormat é criado. "

Assim, em vez de começar a fantasia com o ano da data que está sendo analisado, eles apenas definir o calendário interno de volta 80 anos por padrão. Essa parte não está documentado, por si só, mas quando você sabe sobre ele, as peças todas se encaixam.

Foi útil?

Solução

Eu não sei por que Tom diz que "é algo a ver com a serialização", mas ele tem a linha direita:

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

É linha 813 em SimpleDateFormat.java, que é muito tarde no processo. Até esse ponto, o ano está correta (como é o resto da parte data), então é diminuída por 80.

Aha!

A chamada para parseAmbiguousDatesAsAfter() é a mesma função privada que as chamadas set2DigitYearStart():

/* 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);
}

Agora eu ver o que está acontecendo. Peter, em seu comentário sobre "maçãs e laranjas", estava certo! O ano em SimpleDateFormat é o primeiro ano do "século default", o intervalo no qual uma cadeia de ano de dois dígitos (por exemplo, "1/12/14") é interpretado para ser. Consulte http: / /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

Assim, em um triunfo da "eficiência" mais clareza, o ano no SimpleDateFormat é usado para armazenar "o início do período de 100 anos em que anos de dois dígitos são analisados", não o ano em curso!

Obrigado, isso foi divertido - e, finalmente me levou a instalar a fonte jdk (. Eu só tenho espaço total de 4GB na minha partição /)

Outras dicas

Você está investigando o comportamento interno. Se isso vai para fora da API publicada, em seguida, você está vendo coisas indefinido, e você não deve se preocupar com isso.

Além disso, acredito que o ano de 1929 é usado para considerar quando a interpretar um ano de dois dígitos como estando no 19xx em vez do 20xx.

SimpleDateFormat tem estado interno mutável. É por isso que eu evitá-lo como a peste (eu recomendo Joda Tempo ). Este calendário interno é provavelmente usada durante o processo de analisar uma data, mas não há nenhuma razão que iria ser inicializado para algo em particular antes de ter analisado uma data.

Aqui está algum 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());
    }
}

Olhando através SimpleDateFormat parece que é algo a ver com a serialização:

/* 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 acima código é comparar maçãs e peras

O primeiro fornece-lhe uma ferramenta para analisar String em Datas e vice-versa O segundo é um DateUtility que permite que você manipule datas

Não há realmente uma razão para que o deve fornecer uma saída similar.

Compará-lo com o seguinte

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

ambas as linhas irá imprimir um String, mas logicly você não iria esperar o mesmo resultado

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top