لماذا يحتوي كائن SimpleedTeFormat الجديد على تقويم مع السنة الخطأ؟

StackOverflow https://stackoverflow.com/questions/1258441

سؤال

جئت إلى سلوك غريب تركتني فضولي ودون تفسير مرضي حتى الآن.

بالنسبة للبساطة، فقد خفضت الأعراض التي لاحظتها على الكود التالي:

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

عندما أقوم بتشغيل هذا الرمز، أحصل على شيء مشابه للغاية للإخراج التالي:

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]

(يحدث نفس الشيء إذا قدمت سلسلة تنسيق صالحة مثل "yyyy-MM-dd" إلى simpleedateformat.)

سامح الخطوط غير لفتة مروعة، لكنها أسهل طريقة لمقارنة الاثنين. إذا قمت بالتمرير إلى حوالي 2 / 3DDS من الطريق، فسترى أن التقويمات لها قيم عام 1929 و 2009، على التوالي. (هناك عدد قليل من الاختلافات الأخرى، مثل أسبوع السنة، يوم من أيام الأسبوع، وإزاحة DST.) كلاهما مثيلات من الواضح أن GregorianCalendar، ولكن السبب وراء تختلف اختلافها أمر محير.

من ما يمكنني إخباره بالتكثيف ينتج دقيقا عند تمرير كائنات تاريخ التنسيق إليه. من الواضح أن الوظائف الصحيحة هي أكثر أهمية من السنة المرجعية الصحيحة، ولكن التناقض هو disconcerting رغم ذلك. لا أعتقد أنه يجب علي ضبط التقويم على شكل تاريخ جديد تماما للحصول على السنة الحالية ...

لقد قمت باختبار ذلك على أجهزة Mac مع Java 5 (OS X 10.4، PowerPC) و Java 6 (OS X 10.6، Intel) مع نفس النتائج. نظرا لأن هذه هي API مكتبة Java، أفترض أنه يتصرف نفسه على جميع المنصات. أي نظرة ثاقبة ما إلى ما يجب عليك هنا؟

(ملحوظة: هذا حتى السؤال مرتبط إلى حد ما، ولكن ليس هو نفسه.)


يحرر:

ساعدت الإجابات أدناه كل السلوك. اتضح أن جافادوك simpleedateformat. في الواقع وثيقة هذا إلى حد ما:

"للتحلل مع نمط السنة المختصرة (" Y "أو" YY ")، يجب أن يفسر SimpleedTeFormat السنة المختصرة بالنسبة إلى القرن. هل هذا من خلال ضبط التواريخ ليكون في غضون 80 سنة قبل و 20 سنة بعد مرور الوقت أنشئ."

لذلك، بدلا من الحصول على تخليص مع سنة التاريخ التي يتم تحليلها، فإنها تعرض للتو التقويم الداخلي مرة أخرى 80 عاما افتراضيا. هذا الجزء غير موثق في حد ذاته، ولكن عندما تعرف عن ذلك، فإن القطع تناسبها معا.

هل كانت مفيدة؟

المحلول

لست متأكدا لماذا يقول توم "إنه شيء له علاقة بالسلسلة"، لكن لديه الخط الأيمن:

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

إنه خط 813 في simpleedateformat.java، وهو متأخر جدا في هذه العملية. ما يصل إلى هذه النقطة، السنة صحيحة (كما هو بقية جزء التاريخ)، ثم تنقص 80.

آها!

دعوة إلى parseAmbiguousDatesAsAfter() هي نفس الوظيفة الخاصة التي 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);
}

الآن أرى ما يحدث. بيتر، في تعليقه عن "التفاح والبرتقال"، كان على حق! السنة في SimpleDateFormat هي السنة الأولى من "القرن الافتراضي"، والتي تغطيها سلسلة من السنة المكونة من رقمين (على سبيل المثال، "1/12/14"). يرى http://java.sun.com/j2se/1.4.2/docs/api/java/text/simpledateformat.html#get2digityearstart٪28٪29. :

لذلك في انتصار "الكفاءة" على الوضوح، يتم استخدام السنة في SimpleDateFormat لتخزين "بداية الفترة من 100 عام والتي يتم تحليلها لسنتي رقمين"، وليس العام الحالي!

شكرا، كان هذا ممتعا - وأخيرا حصلني على تثبيت مصدر JDK (ليس لدي سوى إجمالي مساحة 4 جيجابايت على بلدي / تقسيم.)

نصائح أخرى

أنت تحقق في السلوك الداخلي. إذا خرج هذا خارج API المنشور، فأنت ترى أشياء غير محددة، ويجب ألا تهتم بذلك.

بخلاف ذلك، أعتقد أن عام 1929 يستخدم للنظر فيها عند تفسير عامين رقمين على أنه في 19xx بدلا من 20xx.

SimpledateFormat لديه حالة داخلية مطولة. هذا هو السبب في أنني أتجنب ذلك مثل الطاعون (أوصي جودا الوقت). من المحتمل أن يستخدم هذا التقويم الداخلي خلال عملية تحليل التاريخ، ولكن ليس هناك سبب لاحتي من أي شيء على وجه الخصوص قبل أن يحلل تاريخا.

إليك بعض الكود لتوضيح:

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

تبحث من خلال simpleedateformat يبدو أنه شيء يجب القيام به مع التسلسل:

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

مقارنة الرمز أعلاه هو مقارنة التفاح والكمثرى

يوفر لك الأول أداة لتحليل السلسلة إلى التواريخ والعكس بالعكس الثاني هو مؤقت يسمح لك بتلاعب بالتواريخ

ليس هناك حقا سبب يجب أن يوفر الإنتاج المماثل.

قارنه مع ما يلي

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

سيقوم كلا الخطين بإخراج سلسلة ولكن منطقيا لن تتوقع نفس النتيجة

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top