Challenge - Date is obsolete


#1

I managed to finish the easy challenge, but spent way too much time on how to update the Crime.Date object with a new time. It is already really convoluted how the DatePickerFragment has to use a Calendar and GregorianCalendar to get/set the various ints of a date, but it is just as bad with trying to update the time part of a Date object.

I ended up with this, after much head banging to get around the “that method/constructor is deprecated in API 1” trying to use a Date object:

    [code]@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
	mDate = (Date)getArguments().getSerializable(EXTRA_DATE);

    // Create a Calendar to get the year, month, and day
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(mDate);
    int hour = calendar.get(Calendar.HOUR);
    int min = calendar.get(Calendar.MINUTE);
    
	View v = getActivity().getLayoutInflater().inflate(
			R.layout.dialog_time, null);		
	
	TimePicker timePicker = (TimePicker) v.findViewById(R.id.dialog_time_timePicker);
	
	timePicker.setCurrentHour(hour);
	timePicker.setCurrentMinute(min);
    timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {       

		@Override
		public void onTimeChanged(TimePicker tp, int hour, int minute) {
		
			Calendar calendar = Calendar.getInstance();
		    calendar.setTime(mDate);
		    int year = calendar.get(Calendar.YEAR);
		    int month = calendar.get(Calendar.MONTH);
		    int day = calendar.get(Calendar.DAY_OF_MONTH);
		    
			mDate = new  GregorianCalendar(year, month, day, hour, minute).getTime();			

            // Update argument to preserve selected value on rotation
            getArguments().putSerializable(EXTRA_DATE, mDate);				
		}
    });

	return new AlertDialog.Builder(getActivity())
			.setView(v)
			.setTitle(R.string.time_picker_title)
			
			.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					sendResult(Activity.RESULT_OK);
				}
			})
			.create();
}

[/code]

So, naturally I couldn’t help but wonder why we are using a class that is 90% deprecated, when we could be using a Calendar for it. I am guessing that the Date object is a lot smaller than a Calendar object, but can’t tell because the API does not tell us what the fields of a Date object are, while the Calendar object has a lot of fields we wouldn’t need.

So, unless we were to look at the source code, we have no idea what is in a Date object.

If efficiency is the goal, it would be a lot more efficient to just write our own class for a date. We only need the hour, minutes, month, day, and year (5 ints). We could write methods that would encapsulate the convoluted mess we need to go through to format the date for display using the Calendar and GregorianCalendar. Then when/if Google fixes this mess, it would be easier to update the code to the new stuff.

In short, it would have taken me less time to write such a class then figure out how to use the other classes to just update the time in a Date object.


#2

Ooops! I just realized that Date is in java.util, not an android library.


#3

Here it is. How you just need to modify Crime to have a CiDate and the various Controller classes to use its methods.

[code]import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import android.text.format.DateFormat;

public class CiDate implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int mYear;
private int mMonth;
private int mDay;
private int mHour;
private int mMinute;

public CiDate() {
	//Calendar calendar = Calendar.getInstance();
	this(Calendar.getInstance().getTimeInMillis());
}

public CiDate(int year, int month, int day, int hour, int minute) {
	mYear = year;
	mMonth = month;
	mDay = day;
	mHour = hour;
	mMinute = minute;
}

public CiDate(long milliseconds) {
	Date date = new Date(milliseconds);
	Calendar calendar = Calendar.getInstance();
	calendar.setTime(date);
	mYear = calendar.get(Calendar.YEAR);
	mMonth = calendar.get(Calendar.MONTH);
	mDay = calendar.get(Calendar.DAY_OF_MONTH);
	mHour = calendar.get(Calendar.HOUR_OF_DAY);
	mMinute = calendar.get(Calendar.MINUTE);		 
}

public long getMilliseconds() {
	GregorianCalendar calendar = new GregorianCalendar(mYear, mMonth, mDay, mHour, mMinute);
	return calendar.getTimeInMillis();
}

public CharSequence getFormattedDate(String formatString) {
	return DateFormat.format(formatString, new GregorianCalendar(mYear, mMonth, mDay, mHour, mMinute).getTime());
}

public Date getDate() {
	return new GregorianCalendar(mYear, mMonth, mDay, mHour, mMinute).getTime();
}

public int getYear() {
	return mYear;
}

public void setYear(int year) {
	mYear = year;
}

public int getMonth() {
	return mMonth;
}

public void setMonth(int month) {
	mMonth = month;
}

public int getDay() {
	return mDay;
}

public void setDay(int day) {
	mDay = day;
}

public int getHour() {
	return mHour;
}

public void setHour(int hour) {
	mHour = hour;
}

public int getMinute() {
	return mMinute;
}

public void setMinute(int minute) {
	mMinute = minute;
}

}
[/code]


#4

I am curious why no one responded to this thread. I have been struggling with the same issues and have come to similar solution. Just curious if this is what the author’s intended? Or if they believe there is another way.


#5

Thanks for posting here - lately I’ve been pretty good about keeping up, but there are some old posts that fell through the cracks. Let me know if you find any others that are languishing.

This is really more of a Java question, and has to do with the history of the Date class in Java. The regular Java javadoc for this class is more informative: http://docs.oracle.com/javase/6/docs/api/java/util/Date.html. Essentially, they screwed up in Java 1.0 and had to backtrack.

See, the idea of having one class that represents both a specific point in time and a specific calendar date doesn’t work when you internationalize. That’s because different countries use different calendar systems. So in JDK 1.1, they deprecated all the calendar functionality in Date and split it out into the Calendar class.

So Date is not obsolete. It is still the correct choice when you need to represent a moment in time (as we do in Crime). Users are interested in calendar days, though, not UTC timestamps. So whenever presenting a Date to the user, or when a user tells you a new calendar day, it’s necessary to use a Calendar to convert between Date (which is what you need in your data) and calendar days (which are what make sense to your user).

I haven’t tried it out, but at a glance I believe you could shorten RickDroid’s date code in onTimeChanged to the following:

Calendar calendar = Calendar.getInstance();
calendar.setTime(mDate);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
mDate = calendar.getTime();

#6

This has all been VERY helpful. Although I also spent hours banging my head trying to figure out the best approach to this simple challenge, I was glad to see I wasn’t the only one. One question I have is this: Regardless of how the time is set in OnTimeChanged, regardless, isn’t it true that back in DatePicker, every time we change the date, the time field will always be reset since the new GregorianCalendar called doesn’t specify the time? That is unless, we replace:

mDate = new GregorianCalendar(year, month, day).getTime();

With the following:

Calendar calendar = Calendar.getInstance();
calendar.setTime(mDate);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
mDate = calendar.getTime();

I would propose using the GregorianCalendar as follows:

mDate = new GregorianCalendar(year, month, day, hour, minute).getTime();

BUT, I get the following error with respect to how OnDateChangedListener must be used:

  • The type new DatePicker.OnDateChangedListener(){} must implement the inherited abstract
    method DatePicker.OnDateChangedListener.onDateChanged(DatePicker, int, int, int)

If anyone has a better, more elegant suggestion, I’m all for it.

Finally, has anyone run into the issue of the app crashing when rotating to landscape while updated the specific crime? Not sure why, but I seem to have that issue as well. Thanks in advance!