Question

Je suis tombé sur un comportement étrange qui m'a laissé curieux et sans explication satisfaisante encore.

Par souci de simplicité, j'ai réduit les symptômes que j'ai remarqué au code suivant:

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

Quand je lance ce code, je reçois quelque chose de très similaire à la sortie suivante:

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 même chose se produit si je fournis une chaîne de format valide comme "yyyy-MM-dd" à SimpleDateFormat.)

Pardonne les lignes affreuses non emballage, mais il est la meilleure façon de comparer les deux. Si vous faites défiler à environ 2 / 3rds de la façon plus, vous verrez que les calendriers ont des valeurs de 1929 et ANNÉE 2009, respectivement. (Il y a quelques autres différences, comme la semaine de l'année, le jour de la semaine et l'heure d'été compensés.) Les deux sont évidemment des cas de GregorianCalendar, mais la raison pour laquelle ils diffèrent laisse perplexe.

D'après ce que je peux dire au produit formatter précis lors du formatage des objets de date qui lui sont transmises. De toute évidence, la fonctionnalité correcte est plus importante que l'année de référence correcte, mais l'écart est déconcertant quand même. Je ne pense pas que je dois mettre le calendrier sur une date formatter flambant neuf juste pour l'année en cours ...

Je l'ai testé ce sur Mac avec Java 5 (OS X 10.4, PowerPC) et Java 6 (OS X 10.6, Intel) avec les mêmes résultats. Comme il est une API de bibliothèque Java, je suppose qu'il se comporte de la même sur toutes les plateformes. Toute idée sur ce qui est en cours de réalisation ici?

(Note:. Cette question SO est un peu lié, mais pas le même)


Modifier

Les réponses ci-dessous tous ont aidé à expliquer ce comportement. Il se trouve que le Javadocs SimpleDateFormat documenter ce fait dans une certaine mesure:

  

« Pour l'analyse syntaxique avec le motif de l'année abrégé ( » y » ou « yy »), SimpleDateFormat doit interpréter l'année abrégée par rapport à un siècle. Elle le fait en ajustant aux dates à moins de 80 ans avant et 20 ans après le temps l'instance SimpleDateFormat est créé. "

Alors, au lieu d'obtenir de fantaisie avec l'année de la date en cours d'analyse, ils ont juste mis le calendrier interne de retour de 80 ans par défaut. Cette partie n'est pas documenté en soi, mais quand vous en savez, les pièces toutes ensemble.

Était-ce utile?

La solution

Je ne sais pas pourquoi Tom dit « il est quelque chose à voir avec la sérialisation », mais il a la ligne droite:

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

Il est la ligne 813 dans SimpleDateFormat.java, ce qui est très tard dans le processus. Jusqu'à ce moment-là, l'année est correct (comme le reste de la partie date), il est décrémenté de 80.

Aha!

L'appel à parseAmbiguousDatesAsAfter() est la même fonction privée qui set2DigitYearStart() appels:

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

Maintenant, je vois ce qui se passe. Pierre, dans son commentaire sur les « pommes et des oranges », avait raison! L'année SimpleDateFormat est la première année du « siècle par défaut », la plage dans laquelle une chaîne d'année à deux chiffres (par exemple, « 12/01/14 ») est interprété comme. Voir http: / /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

Ainsi, dans un triomphe de « l'efficacité » sur la clarté, l'année dans le SimpleDateFormat est utilisé pour stocker « le début de la période de 100 ans dans laquelle deux années de chiffres sont analysés », pas l'année en cours!

Merci, ce fut amusant - et enfin m'a fait installer la source jdk (. Je n'ai que 4 Go espace total sur ma partition /)

Autres conseils

Vous enquêtez sur le comportement interne. Si cela se passe en dehors de l'API alors publié vous voyez des choses non définie, et vous ne devriez pas en soucier.

Autre que cela, je belive que l'année 1929 est utilisée pour considérer quand interpréter deux chiffres de l'année comme dans le 19XX au lieu du 20xx.

SimpleDateFormat a mutable état interne. Voilà pourquoi je l'éviter comme la peste (je vous recommande Joda temps ). Ce calendrier interne est probablement utilisé au cours du processus d'analyse d'une date, mais il n'y a aucune raison pour laquelle il serait initialisé à quelque chose en particulier avant qu'il ait analysé une date.

Voici un code pour illustrer:

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

En regardant à travers SimpleDateFormat il semble que c'est quelque chose à voir avec la sérialisation:

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

on compare le code ci-dessus est de comparer des pommes et poires

La première vous fournit un outil pour analyser la chaîne en dates et vice-versa Le second est un DateUtility qui vous permet de manipuler des dates

Il n'y a pas vraiment une raison pour laquelle la sortie devrait fournir similaire.

Comparez avec les éléments suivants

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

Les deux lignes de sortie d'une chaîne, mais vous ne seriez pas logicly attendre le même résultat

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top