I'm debugging an issue where the CalendarView
in a DatePicker
moves to October 1964 if there is a non-default minimum date set. This reproduces at least on API17 Nexus7 emulator but there are reports about this issue on other devices where the platform style includes both spinners and calendar view in date picker.
Here's a code snippet demonstrating the issue:
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();
p.setMinDate(min.getTime());
}
}
...
new DatePickerDialog1964(context).show();
Screen shot:
Of course, the expectation is that the spinners and calendar view would be showing the same date.
I'll keep on debugging this but if any SO users have experience or other insight, please share.
Related:
It's not 1964 but 2100 formatted badly. CalendarView
has bugs. formatDateRange()
does not work past 2038.
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);
}
}
// Calendar view is a cascade of bugs.
// Work around that by explicitly disabling it.
datePicker.setCalendarViewShown(false);
CalendarView
uses android.text.format.DateUtils.formatDateRange()
for producing the month/year name in the header. Calendar view only updates the header when the month number changes. For example, if the month number is the same but the year changes, the header is not updated.
Somewhere during layout phase, the underlying ListView
invokes OnScrollListener
on the calendar view as if the list was scrolled to the end. If the month number is changed, the header is updated with the test code above, the millis value passed to formatDateRange()
is 4131043200000
which is somewhere in late 2100, and 2100 is the default maximum for DatePicker
.
formatDateRange()
in turn uses android.text.format.Time
as its internal calendar representation, specifically setting the millis using its set()
method. I did not check the underlying native code, but my educated guess is that the passed in milliseconds value gets truncated to 32-bit time_t
seconds value with the inherent Year 2038 problem, wrapping to a value of a little more than 62 years. Time
itself is using struct tm
which represents years as an int
since 1900, thus resulting in a Time
in the 1960s which then gets formatted in the header.
This can be reproduced with plain DatePicker
with CalendarView
without min date set. Just use the spinners to move the year to 2038 or later, change the month (to trigger the header update) and you'll see the year in header wrap to 1902.
Now, the question remains why setting a minimum date makes the calendar view scroll to maximum date. Answer seems to be the calendar view's week ListView
adapter. It indexes weeks from the minimum date, but when the minimum date changes, the adapter's notifyDataSetChanged()
is not called. So the workaround #1 above works because: