Domanda

mi sono imbattuto in uno strano comportamento che mi ha lasciato curioso e senza una spiegazione soddisfacente ancora.

Per semplicità, ho ridotto i sintomi che ho notato al seguente codice:

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 ho eseguito questo codice, ottengo qualcosa di molto simile al seguente output:

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]

(La stessa cosa accade se fornisco una stringa di formato valida come "yyyy-MM-dd" a SimpleDateFormat.)

Perdona le linee di confezionamento non orrendi, ma è il modo più semplice per confrontare i due. Se si scorre a circa 2 / 3rds del modo sopra, vedrete che i calendari hanno valori anno di, rispettivamente, 1929 e 2009. (Ci sono alcune altre differenze, come ad esempio settimana dell'anno, giorno della settimana, e compensare l'ora legale.) Entrambi sono, ovviamente, i casi di GregorianCalendar, ma il motivo per cui si differenziano è sconcertante.

Da quello che posso dire al formattatore produce precisi quando la formattazione oggetti Date passati ad esso. Ovviamente, corretta funzionalità è più importante rispetto all'anno di riferimento corretto, ma la discrepanza è sconcertante comunque. Non vorrei pensare che avrei dovuto impostare il calendario in una data nuova di zecca formattatore solo per ottenere l'esercizio in corso ...

Ho provato questo sui Mac con Java 5 (OS X 10.4, PowerPC) e Java 6 (OS X 10.6, Intel) con gli stessi risultati. Dal momento che questa è una libreria Java API, suppongo si comporta lo stesso su tutte le piattaforme. Tutta la comprensione su che cosa sta piedi qui?

(Nota:. questa domanda SO è in qualche modo correlata, ma non lo stesso)


Modifica

Le risposte di seguito tutti contribuito a spiegare questo comportamento. Si scopre che la Javadocs per SimpleDateFormat in realtà documentare questo in una certa misura:

  

"Per l'analisi con il modello dell'anno abbreviato (" y" o 'aa'), SimpleDateFormat deve interpretare l'anno relativo abbreviato a qualche secolo. Lo fa regolando le date per essere entro 80 anni prima e 20 anni dopo il tempo viene creata l'istanza SimpleDateFormat ".

Così, invece di ottenere fantasia con l'anno della data di essere analizzato, hanno solo impostare il calendario interno indietro di 80 anni per impostazione predefinita. Quella parte non è documentato per se, ma quando si sa a questo proposito, i pezzi si incastrano tutti.

È stato utile?

Soluzione

Non so il motivo per cui Tom dice "è qualcosa a che fare con la serializzazione", ma ha la giusta linea:

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

E 'la linea 813 in SimpleDateFormat.java, che è molto tardi nel processo. Fino a quel momento, l'anno è corretto (come il resto della parte data), allora è decrementato di 80.

Aha!

La chiamata alla parseAmbiguousDatesAsAfter() è la stessa funzione privata che set2DigitYearStart() chiamate:

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

Ora vedo quello che sta succedendo. Pietro, nel suo commento sulle "mele e arance", aveva ragione! Anno in SimpleDateFormat è il primo anno del "secolo default", l'intervallo in cui una stringa anno a due cifre (ad esempio, "1/12/14") viene interpretato sia. Vedere http: / /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

Quindi, in un trionfo di "efficienza" sopra la chiarezza, l'anno nel SimpleDateFormat viene utilizzata per memorizzare "l'inizio del periodo di 100 anni, in cui vengono analizzati due anni cifre", non è l'anno in corso!

Grazie, questo è stato divertente - e alla fine mi ha fatto installare la fonte JDK (. Ho solo 4 GB di spazio totale sul mio divisorio /)

Altri suggerimenti

Si sta indagando comportamento interno. Se questo va al di fuori della API pubblicata allora che state vedendo roba indefinita, e non si dovrebbe cura su di esso.

Oltre a questo, I belive che l'anno 1929 è utilizzato per prendere in considerazione quando a interpretare un anno a due cifre come nel 19xx al posto del 20xx.

SimpleDateFormat ha stato interno mutevole. Questo è il motivo per cui evito come la peste (mi raccomando Joda Tempo ). Questo calendario interno è probabilmente usato durante il processo di analisi di un appuntamento, ma non c'è motivo sarebbe inizializzato a qualcosa in particolare prima che abbia analizzato una data.

Ecco un po 'di codice per illustrare:

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

Guardando attraverso SimpleDateFormat sembra come se fosse qualcosa a che fare con la serializzazione:

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

confrontando sopra il codice è confrontare le mele e le pere

La prima si fornisce uno strumento per analizzare la stringa in date e viceversa Il secondo è un DateUtility che permette di manipolare le date

In realtà non è un motivo per cui la dovrebbe fornire un risultato simile.

Confronto con il seguente

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

Entrambe le linee di uscita di una stringa, ma logicly si wouldnt aspetta lo stesso risultato

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top