Reputation: 12480
I want to make calendar view in order to support touch interaction. So I'd like to build new custom calendar view. I tried to make mapping function between view offset and real date value.
Here is my idea: If I can compute the number of weeks since base date(in my case, 1989-12-31), it is easy to know offset. HEIGHT_FOR_WEEK * NUM_OF_WEEK is very simple computation to know exact offset.
My problem is this: First I got milliseconds value from base date. And I set the milliseconds to another calendar object. I expected same date from that object. But actually it was different date.
mBaseDateInMillis = mBaseDate.getTimeInMillis();
mAnotherDate.setTimeInMillis(mBaseDateInMillis);
/* I expect mBaseDate == mAnotherDate.
* but it was different.
*/
Here is my code:
public class CalendarCoordinate {
public static final long ONEWEEK_IN_MILLISECONDS = 60 * 60 * 24 * 7 * 1000;
public Calendar mBaseDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
public long mBaseDateInMillis = 0;
public Calendar mDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
public int mWeekHeight = 30;
/**
* CTOR
*/
public CalendarCoordinate() {
/* Base date is 1989-12-31 0, 0, 0
* It was Sunday and offset 0 will be mapped onto this day.
*/
mBaseDate.set(Calendar.MILLISECOND, 0);
mBaseDate.set(1989, 12, 31, 0, 0, 0);
mBaseDateInMillis = mBaseDate.getTimeInMillis();
Log.v(TAG, "BaseDate:" + mBaseDate.toString());
}
/**
* Compute DATE from Y-Offset
* @param yOffset
* @return
*/
public Calendar dateFromYOffset(int yOffset) {
long nthWeeks = yOffset / mWeekHeight;
long millsSinceBaseDate = nthWeeks * ONEWEEK_IN_MILLISECONDS;
mDate.clear();
mDate.set(Calendar.MILLISECOND, 0);
mDate.setTimeInMillis(mBaseDateInMillis + millsSinceBaseDate);
/* We SHOULD call to update mDate internal data structure.
* Java is really strange for this thing
**/
mDate.getTimeInMillis();
return mDate;
}
/**
* Compute Y-Offset from DATE
* @param date
* @return
*/
public long yOffsetFromDate(Calendar cal) {
long mills = cal.getTimeInMillis();
long nthWeeks = (mills - mBaseDateInMillis)/ONEWEEK_IN_MILLISECONDS;
return nthWeeks * mWeekHeight;
}
}
Anybody can help me? I'm not a good Java programmer.
Upvotes: 2
Views: 12669
Reputation: 338775
long weeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) ); // Results: 2
Do not work in a count-since-epoch such as milliseconds. Confusing, cloaks bugs, and ignores issues such as time zones.
Let a good date-time library do the heavy-lifting in these calculations.
You are using troublesome old date-time classes such as java.util.Calendar
. These poorly-designed classes have been supplanted by the java.time framework built into Java 8 and later. See Oracle Tutorial. Much of the java.time functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
ChronoUnit
The ChronoUnit
class calculates elapsed time such as number of whole weeks between a pair of LocalDate
(date-only, no time-of-day nor time zone) values.
LocalDate start = LocalDate.of ( 2016 , 1 , 1 );
LocalDate stop = start.plusDays ( 17 ); // Ex: 13 days = 1 week. 14 days = 2 weeks.
long weeks = ChronoUnit.WEEKS.between ( start , stop );
Dump to console.
System.out.println ( "start: " + start + " | stop: " + stop + " | weeks: " + weeks );
start: 2016-01-01 | stop: 2016-01-18 | weeks: 2
If you want a number of weeks since 1989-12-31
, use that as the start
object seen above.
LocalDate start = LocalDate.of( 1989 , 12 , 31 );
But I notice your base date is the last day of the year. Tip: spans of time are generally best handled with the Half-Open approach where the beginning is inclusive while the ending is exclusive. So you may want to use 1990-01-01
as your base date (I do not know your business logic, so just a guess on my part).
So you first two weeks would be this (1st - 15th):
long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1990 , 1 , 1 ) , LocalDate.of ( 1990 , 1 , 15 ) );
…rather than this (31st - 14th):
long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) );
Upvotes: 0
Reputation: 95519
This is because you need to use the "equals" method to compare different objects. Using operator "==" will tell you if the objects are identical (do they reside in the exact same memory location), while the "equals" comparison function will tell you if the two objects are logically equivalent.
Upvotes: 3
Reputation: 28419
This statement confuses me:
/* I expect mBaseDate == mAnotherDate.
* but it was different.
*/
Are you actually trying to check for equality by doing the comparison: if (mBaseDate == mAnotherDate) { System.out.println("They are the same"); }
If so, your issue is that you are misunderstanding how the "==" operator works in Java. It compares references, rather than comparing the underlying object data, and since these are different objects (with the same values) that will always be false. For a lot more details, see the Java Notes on comparison operators.
Also, these lines look really suspicious to me:
/* We SHOULD call to update mDate internal data structure.
* Java is really strange for this thing
**/
mDate.getTimeInMillis();
I would really be surprised if Android had a bug requiring you to do this, but I guess anything is possible. What kind of problems do you have without this call?
Upvotes: 5