なぜ新しいのSimpleDateFormatオブジェクトが間違った年とカレンダーが含まれていますか?
-
12-09-2019 - |
質問
私は好奇心とまだとして十分な説明なしで私を残した奇妙な行動に出くわしました。
簡単にするために、私は次のコードに気づいた症状を軽減しました。
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]
(私はSimpleDateFormatのに"yyyy-MM-dd"
のような有効なフォーマット文字列を提供する場合、同じことが起こります。)
恐ろしい非ラッピングラインを許したが、それは2を比較する最も簡単な方法です。あなたは以上の方法の約2 / 3rdsにスクロールする場合は、それぞれのカレンダーは1929年と2009年のYEAR値を持っていることがわかります。 (そこな年の週、曜日など他のいくつかの違いが、あり、そしてDSTオフセット。)どちらも明らかのGregorianCalendarのインスタンスであるが、それらが異なる理由は不可解されます。
私はそれに渡されたDateオブジェクトをフォーマットするときフォーマッタは正確な生産伝えることができるものから。明らかに、正しい機能は、正しい基準年よりも重要であるが、相違はそれにもかかわらず当惑です。私はちょうど現在の年を取得するために、ブランドの新しい日付フォーマッタ上のカレンダーを設定する必要があるだろうと思っていないでしょう...
私は同じ結果とJava 5の(OS X 10.4、PowerPCの)およびJava 6(OS X 10.6、インテル)を搭載したMac上でこれをテストしてみました。これは、JavaライブラリのAPIですので、私はそれがすべてのプラットフォームで同じように動作を前提としています。ここで進行中何上の任意の洞察力?
(注:これはSO のが幾分関連している質問が、同じではない)
<時間>の編集の
すべての以下の答えは、この動作を説明する助けました。これはてSimpleDateFormat のJavadocことが判明しますA>実際にはある程度、これを文書化します:
代わりに、日付の年が解析されていると空想取得する「(y」または 『yy』)略し年パターンで解析するための」、SimpleDateFormatは特定の世紀に合わせて短縮年を解釈する必要があり。それは、日付を調整することで、これは時間の後に80年前と20年の内にあることがありませんSimpleDateFormatのインスタンスが作成されます。 "
そう、彼らはただ、デフォルトでは80年前に内部のカレンダーを設定します。その一部は、それ自体が文書化されていませんが、あなたはそれについて知っているとき、作品はすべて一緒にフィットします。
解決
私はトムが「それは直列化とは何かだ」と言う理由はわからないんだけど、彼は右の行があります:
private void initializeDefaultCentury() {
calendar.setTime( new Date() );
calendar.add( Calendar.YEAR, -80 );
parseAmbiguousDatesAsAfter(calendar.getTime());
}
これは非常に遅く、プロセスであるSimpleDateFormat.javaでライン813、です。その時点までは、今年は(日付部分の残りの部分のように)、それは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年は、「デフォルトの世紀」への2桁の年文字列(例えば、「1/12/14」)範囲の最初の年があると解釈されます。 /: HTTPを参照してください。 /java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29するます:
明快オーバー「効率」の勝利でそう、SimpleDateFormatの年は現在の年ではなく、「2桁の年が解析されているに100年の期間の開始」を格納するために使用された!
おかげで、これは楽しかった - そして最後にJDKのソースをインストールするために私を得た(。私は私の/
パーティションの合計4GBの容量を持っている)。
他のヒント
あなたは内部の挙動を調査しています。これは、公開APIの外に行くなら、あなたは未定義のものを見ている、とあなたはそれを気にしてはいけません。
それ以外は、私は1929年ではなく、20xx年の19XXにあるものとして2桁の年を解釈する際に考慮するために使用されていることを信じています。
の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());
}
}
のSimpleDateFormatてみると、それはシリアル化とは何かだように思えるます:
/* 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());
コード上方比較するリンゴ及びナシを比較している
最初はあなたの日付とその逆に文字列を解析するためのツールを提供しています 二つ目は、あなたが日付を操作することができますDateUtilityです。
同じような出力を提供しなければならない理由は本当にありません。
次でそれを比較。
System.out.println(new String() );
System.out.println(new Date().toString() );
出力文字列をしますが、logiclyあなたが同じ結果を期待してwouldntの両方の線