Android java.util.Calendar – 时差

我想制作日历视图以支持触摸交互。 所以我想构建新的自定义日历视图。 我试图在视图偏移和实际日期值之间建立映射函数。

这是我的想法:如果我可以计算自基准日期以来的周数(在我的情况下,1989-12-31),很容易知道偏移量。 HEIGHT_FOR_WEEK * NUM_OF_WEEK是非常简单的计算,可以知道精确的偏移量。

我的问题是这样的:首先我从基准日期获得毫秒值。 我将毫秒设置为另一个日历对象。 我期望该对象的日期相同。 但实际上它是不同的日期。

mBaseDateInMillis = mBaseDate.getTimeInMillis(); mAnotherDate.setTimeInMillis(mBaseDateInMillis); /* I expect mBaseDate == mAnotherDate. * but it was different. */ 

这是我的代码:

 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; } } 

有人可以帮帮我吗? 我不是一个优秀的Java程序员。

这句话让我很困惑:

 /* I expect mBaseDate == mAnotherDate. * but it was different. */ 

你是否真的试图通过比较来检查是否相等:if(mBaseDate == mAnotherDate){System.out.println(“它们是相同的”); }

如果是这样,那么您的问题是您误解了“==”运算符在Java中的工作原理。 它比较引用,而不是比较底层对象数据,因为它们是不同的对象(具有相同的值),它们总是为假。 有关更多详细信息,请参阅有关比较运算符的Java Notes 。

此外,这些线条对我来说真的很可疑:

 /* We SHOULD call to update mDate internal data structure. * Java is really strange for this thing **/ mDate.getTimeInMillis(); 

如果Android有一个错误要求你这样做我真的会感到惊讶,但我想任何事都有可能。 没有这个电话,你有什么问题?

这是因为您需要使用“equals”方法来比较不同的对象。 使用运算符“==”将告诉您对象是否相同(它们是否位于完全相同的内存位置),而“equals”比较函数将告诉您这两个对象是否在逻辑上等效。

TL;博士

 long weeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) ); // Results: 2 

避免从时代算起来

不要在count-since-epoch(例如毫秒)中工作。 令人困惑,掩盖错误,并忽略时区等问题。

让一个好的日期时间库在这些计算中做繁重的工作。

java.time

您正在使用麻烦的旧日期时间类,例如java.util.Calendar 。 这些设计糟糕的类已被Java 8及更高版本中内置的java.time框架所取代。 请参阅Oracle教程 。 许多java.timefunction已经在ThreeTen-Backport中反向移植到Java 6和7,并在ThreeTenABP中进一步适应Android。

ChronoUnit

ChronoUnit类计算经过的时间,例如一对LocalDate (仅限日期,没有时间和时区)值之间的整周数。

  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 ); 

转储到控制台。

  System.out.println ( "start: " + start + " | stop: " + stop + " | weeks: " + weeks ); 

开始时间:2016-01-01 | 停止:2016-01-18 | 周:2

如果您想要自1989-12-31以来的几周,请将其用作上面所示的start对象。

 LocalDate start = LocalDate.of( 1989 , 12 , 31 ); 

半开

但我注意到你的基准日期是一年中的最后一天。 提示:时间跨度通常最好采用半开放方法处理,其中开头是包容性的,而结尾是独占的 。 因此,您可能希望使用1990-01-01作为基准日期(我不知道您的业务逻辑,所以我只是猜测一下)。

所以你前两周就是这个(1日至15日):

 long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1990 , 1 , 1 ) , LocalDate.of ( 1990 , 1 , 15 ) ); 

…而不是这个(31-14):

 long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) );