TL;DR
1964年ではなく、2100年のフォーマットが悪い。CalendarView
バグがあります。formatDateRange()
2038 年以降は機能しません。
回避策 #1
class DatePickerDialog1964 extends DatePickerDialog {
DatePickerDialog1964(Context c) {
super(c, null, 2013, 4, 21);
@SuppressWarnings("deprecation")
Date min = new Date(2013-1900, 4, 21);
DatePicker p = getDatePicker();
CalendarView cv = p.getCalendarView(); // should check for null
long cur = cv.getDate();
int d = cv.getFirstDayOfWeek();
p.setMinDate(min.getTime());
cv.setDate(cur + 1000L*60*60*24*40);
cv.setFirstDayOfWeek((d + 1) % 7);
cv.setDate(cur);
cv.setFirstDayOfWeek(d);
}
}
回避策その2 私が実際に使った
// Calendar view is a cascade of bugs.
// Work around that by explicitly disabling it.
datePicker.setCalendarViewShown(false);
分析
CalendarView
android.text.format.DateUtils.formatDateRange()
ヘッダーで月/年の名前を生成するために使用します。カレンダー ビューは、月番号が変更されたときにのみヘッダーを更新します。たとえば、月番号は同じで年が変わる場合、ヘッダーは更新されません。
レイアウト フェーズのどこかで、リストが最後までスクロールされたかのように、基になるカレンダー ビューListView
が呼び出されます。OnScrollListener
月番号が変更された場合、ヘッダーは上記のテスト コードで更新され、 に渡されるミリ秒値はformatDateRange()
21004131043200000
後半のどこかであり、2100 は のデフォルトの最大値ですDatePicker
。
formatDateRange()
次にandroid.text.format.Time
、内部カレンダー表現として使用し、具体的にはそのset()
メソッドを使用してミリ秒を設定します。私は基礎となるネイティブ コードをチェックしませんでしたが、私の知識に基づく推測では、渡されたミリ秒の値time_t
は固有の2038 年の問題で 32 ビットの秒の値に切り捨てられ、62 年を少し超える値にラップされます。Time
それ自体は、1900 年以降の年struct tm
を表すを使用しているため、ヘッダーでフォーマットされる 1960 年代になります。int
Time
こちらは分日設定なしで無地DatePicker
で再現できます。CalendarView
スピナーを使用して年を 2038 以降に移動し、月を変更して (ヘッダーの更新をトリガーする)、ヘッダー ラップの年が 1902 に表示されます。
ここで、最小の日付を設定すると、カレンダー ビューが最大の日付までスクロールする理由が疑問のままです。答えは、カレンダー ビューの週ListView
アダプターのようです。最小日付から週のインデックスを作成しますが、最小日付が変更されると、アダプターnotifyDataSetChanged()
は呼び出されません。したがって、上記の回避策 #1 は次の理由で機能します。
- 日付を 1 か月以上変更すると、月のヘッダーが変更されます。
- 週の最初の曜日を変更すると、週のリストビューはアダプタのデータが変更されたことを通知します。