Why does a new SimpleDateFormat object contain calendar with the wrong year?

后端 未结 5 546
暖寄归人
暖寄归人 2020-12-20 15:54

I came upon a strange behavior that has left me curious and without a satisfactory explanation as yet.

For simplicity, I\'ve reduced the symptoms I\'ve noticed to th

相关标签:
5条回答
  • 2020-12-20 16:05

    SimpleDateFormat has mutable internal state. This is why I avoid it like the plague (I recommend Joda Time). This internal calendar is probably used during the process of parsing a date, but there's no reason it would be initialized to anything in particular before it has parsed a date.

    Here's some code to illustrate:

    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());
        }
    }
    
    0 讨论(0)
  • 2020-12-20 16:09

    I'm not sure why Tom says "it's something to do with serialization", but he has the right line:

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

    It's line 813 in SimpleDateFormat.java, which is very late in the process. Up to that point, the year is correct (as is the rest of the date part), then it's decremented by 80.

    Aha!

    The call to parseAmbiguousDatesAsAfter() is the same private function that set2DigitYearStart() calls:

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

    Now I see what's going on. Peter, in his comment about "apples and oranges", was right! The year in SimpleDateFormat is the first year of the "default century", the range into which a two-digit year string (e.g, "1/12/14") is interpreted to be. See http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29 :

    So in a triumph of "efficiency" over clarity, the year in the SimpleDateFormat is used to store "the start of the 100-year period into which two digit years are parsed", not the current year!

    Thanks, this was fun -- and finally got me to install the jdk source (I only have 4GB total space on my / partition.)

    0 讨论(0)
  • 2020-12-20 16:15

    Looking through SimpleDateFormat it seems like it's something to do with serialization:

    /* 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());
    }
    
    0 讨论(0)
  • 2020-12-20 16:23
    System.out.println(new SimpleDateFormat().getCalendar());
    System.out.println(new GregorianCalendar());
    

    comparing above code is comparing apples and pears

    The first provides you a tool to parse String into Dates and vice versa The second is a DateUtility that allows you to manipulate Dates

    There is not really a reason why the should provide similar output.

    Compare it with the following

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

    both lines will output a String but logicly you wouldnt expect the same result

    0 讨论(0)
  • 2020-12-20 16:28

    You are investigating internal behaviour. If this goes outside the published API then you are seeing undefined stuff, and you should not care about it.

    Other than that, I belive that the year 1929 is used for considering when to interpret a two digit year as being in the 19xx instead of the 20xx.

    0 讨论(0)
提交回复
热议问题