Frage

Ich kam auf ein seltsames Verhalten, das mich neugierig und ohne befriedigende Erklärung noch verlassen hat.

Der Einfachheit halber habe ich die Symptome reduziert ich auf den folgenden Code bemerkt haben:

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

Wenn ich diesen Code ausführen, bekomme ich etwas sehr ähnlich wie die folgende Ausgabe:

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]

(Das gleiche passiert, wenn ich eine gültige Formatstring wie "yyyy-MM-dd" zu Simple liefern.)

Vergib die horrenden nicht Einwickelstraßen, aber es ist der einfachste Weg, um die beiden zu vergleichen. Wenn Sie etwa 2 / 3rds der Art und Weise über blättern, werden Sie sehen, dass die Kalender haben YEAR Werte von 1929 bzw. 2009. (Es gibt ein paar andere Unterschiede, wie Woche Jahr, Tag der Woche, und DST-Offset.) Beide sind offensichtlich Instanzen GregorianCalendar, aber der Grund, warum sie sich unterscheiden ist rätselhaft.

Von dem, was ich sagen kann, die Forma präzise erzeugt, wenn Datum Formatierung von Objekten an sie übergeben. Offensichtlich ist eine korrekte Funktionalität wichtiger als die richtige Bezugsjahr, aber die Diskrepanz dennoch ist beunruhigend. Ich würde nicht denken, dass ich den Kalender auf einem brandneues Datum Formatter gesetzt haben würde nur das aktuelle Jahr zu bekommen ...

Ich habe dies auf Macs getestet mit Java 5 (OS X 10.4, PowerPC) und Java 6 (OS X 10.6, Intel) mit den gleichen Ergebnissen. Da dies eine Java-Bibliothek API ist, gehe ich davon aus es die auf allen Plattformen gleich verhält. Jeder Einblick auf, was im Gang hier?

. (Anmerkung: Diese Frage SO etwas verwandt ist, aber nicht gleich)


Edit:

Die Antworten unter allen geholfen, dieses Verhalten zu erklären. Es stellt sich heraus, dass die Javadocs für Simple dokumentieren eigentlich diese bis zu einem gewissen Grad:

  

„Denn mit den abgekürzten Jahr Mustern Parsen (“ y“oder‚yy‘), Simple muss die abgekürzte Jahr relativ zu einem Jahrhundert interpretieren. Es tut dies, indem Daten Einstellung innerhalb von 80 Jahren dauern, bis und 20 Jahren nach der Zeit die Simple Instanz erstellt. "

Also, statt mit dem Jahr nach Lust bekommen analysiert werden, sie setzen nur den internen Kalender zurück 80 Jahre standardmäßig. Dieser Teil ist nicht per se dokumentiert, aber wenn man darüber wissen, sind die Stücke alle zusammen passen.

War es hilfreich?

Lösung

Ich bin mir nicht sicher, warum Tom sagt: „es ist etwas mit Serialisierung zu tun“, aber er hat die richtige Linie:

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

Es ist Linie 813 in SimpleDateFormat.java, die sehr spät im Prozess ist. Bis zu diesem Punkt ist das Jahr korrekt ist (wie auch der Rest des Datumsteils ist), dann ist es vermindert um 80.

Aha!

Der Aufruf von parseAmbiguousDatesAsAfter() ist die gleiche private Funktion, die Anrufe 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);
}

Jetzt sehe ich, was los ist. Peter, in seinem Kommentar zum Thema „Äpfel und Orangen“, war richtig! Das Jahr in Simple ist das erste Jahr des „default Jahrhunderts“, in dem der Bereich ein zweistelliger Jahr Zeichenfolge (beispielsweise „1/12/14“) interpretiert werden. Siehe http: / /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

So in einem Triumph der „Effizienz“ über Klarheit ist das Jahr, in dem Simple verwendet „den Beginn des 100-Jahres-Zeitraums, in die zweistelligen Jahre analysiert werden“ zu speichern, nicht das aktuelle Jahr!

Danke, das hat Spaß gemacht - und bekam schließlich mich die jdk Quelle zu installieren (. Ich habe nur 4 GB Gesamtspeicherplatz auf meiner / Partition)

Andere Tipps

Sie untersuchen interne Verhalten. Wenn dies außerhalb der veröffentlichten API geht, dann sind Sie nicht definiert Sachen zu sehen, und man sollte darüber nicht.

Other than that, ich glaube, dass das Jahr 1929 für die Prüfung verwendet wird, wenn ein zweistelliges Jahr als in der 19xx statt der 20xx zu interpretieren.

Simple hat wandelbar internen Zustand. Das ist, warum ich es wie die Pest meiden (ich empfehle Joda Zeit ). Dieser interne Kalender wird wahrscheinlich während des Prozesses verwendet, um ein Datum zu parsen, aber es gibt keinen Grund, warum es vor allem auf etwas initialisiert werden würde, bevor sie ein Datum analysiert hat.

Hier ist ein Code zu veranschaulichen:

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

Beim Blick durch Simple scheint es, wie es ist etwas mit Serialisierung zu tun:

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

obigen Code zu vergleichen ist, Äpfel und Birnen

Vergleich

Der erste liefert Ihnen ein Werkzeug String in Daten und umgekehrt zu analysieren Die zweite ist eine DateUtility, die Sie Daten

manipulieren

Es ist nicht wirklich ein Grund, warum der ähnlichen Ausgang bieten sollte.

Vergleichen Sie es mit den folgenden

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

beide Linien ausgeben einen String aber logicly werden Sie nicht das gleiche Ergebnis erwarten

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top