我正在检索一年中第一周的日期,我发现了非常奇怪的行为。
我在Java控制台应用和Android模拟器中测试了以下代码片段,它会产生不同的输出。
Calendar cal = Calendar.getInstance();
cal.set(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(sdf.format(cal.getTime()));
产生以下输出
Android log cat:2012/09/17(不正确)
Java控制台:2012/01/01(正确)
奇怪的是,如果我在Android和Java中使用以下代码,它会产生相同的正确输出。唯一的区别是我从上面的代码中交换了第2行和第3行。
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
cal.set(Calendar.WEEK_OF_YEAR, 1);
System.out.println(sdf.format(cal.getTime()));
Android log cat:2012/01/01(更正)
Java控制台:2012/01/01(正确)
我很想知道这件事。
提前致谢。
答案 0 :(得分:13)
似乎Calendar类内部有两个数据容器。
protected long time
protected int[] fields
因此,当您致电cal.set(Calendar.WEEK_OF_YEAR, 1)
时,您更改了fields
中的值,而不是类time
中的值。
在Java API中
protected abstract void computeFields()
将当前毫秒时间值时间转换为字段[]中的日历字段值。这样,您就可以使用为日历设置的新时间同步日历字段值。时间不会先重新计算;重新计算时间,然后是字段,调用complete()方法。
我认为在Android computeFields()
的第一种情况下不会在内部调用。
为了检查我的理论,我测试了以下代码:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
Calendar cal = Calendar.getInstance();
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));
cal.set(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(cal);
System.out.println(sdf.format(cal.getTime()));
logcat的:
java.util.GregorianCalendar中[<强>时间= 1348010308802,areFieldsSet =真下,从宽=真,区= org.apache.harmony.luni.internal.util.ZoneInfo [ “空”,mRawOffset = 0,mUseDst = FALSE],Firstdayofweek可= 1,minimalDaysInFirstWeek = 4,ERA == 1,YEAR == 2012,MONTH == 8,的 WEEK_OF_YEAR == 38 下,WEEK_OF_MONTH == 4,DAY_OF_MONTH == 18,DAY_OF_YEAR == 262的 DAY_OF_WEEK == 3 下,DAY_OF_WEEK_IN_MONTH == 3,AM_PM == 1,HOUR == 11,HOUR_OF_DAY = 23,MINUTE == 18,SECOND == 28,微差= = 802,ZONE_OFFSET == 0,DST_OFFSET == 0]
2012年9月18日
java.util.GregorianCalendar中[<强>时间= ?, areFieldsSet =假下,从宽=真,区= org.apache.harmony.luni.internal.util.ZoneInfo [ “空”,mRawOffset = 0,mUseDst = FALSE],Firstdayofweek可= 1,minimalDaysInFirstWeek = 4,ERA == 1,YEAR == 2012年,月== 8,的 WEEK_OF_YEAR == 1 下,WEEK_OF_MONTH == 4,DAY_OF_MONTH == 18,DAY_OF_YEAR == 262的 DAY_OF_WEEK == 3 下,DAY_OF_WEEK_IN_MONTH == 3,AM_PM == 1,HOUR == 11,HOUR_OF_DAY = 23,MINUTE == 18,SECOND == 28,微差= = 802,ZONE_OFFSET == 0,DST_OFFSET == 0]
2012年1月3日
java.util.GregorianCalendar中[<强>时间= ?, areFieldsSet =假下,从宽=真,区= org.apache.harmony.luni.internal.util.ZoneInfo [ “空”,mRawOffset = 0,mUseDst = FALSE],Firstdayofweek可= 1,minimalDaysInFirstWeek = 4,ERA == 1,YEAR == 2012,MONTH == 8,的 WEEK_OF_YEAR == 1 下,WEEK_OF_MONTH == 4,DAY_OF_MONTH == 18,DAY_OF_YEAR == 262的 DAY_OF_WEEK == 1 下,DAY_OF_WEEK_IN_MONTH == 3,AM_PM == 1,HOUR == 11,HOUR_OF_DAY = 23,MINUTE == 18,SECOND == 28,微差= = 802,ZONE_OFFSET == 0,DST_OFFSET == 0]
2012年9月16日
正如我们上面所见,字段中的值已更改,但内部时间表示为?
,表示time
未与fields
同步。
您使用time
方法未同步getTime()
并将其打印出来。
我认为Android中的日历旨在延迟同步,直到真正需要它为止。
<强> ADDED 强>
我在Java API中发现了以下内容:
可以使用三种方法更改日历字段:set(),add()和roll()。
set(f,value)将字段f更改为值。此外,它设置内部成员变量以指示字段f已更改。 虽然字段f立即更改,但在下一次调用get(),getTime()或getTimeInMillis()之前,不会重新计算日历的毫秒数。因此,对set()的多次调用不会触发多次不必要的计算。使用set()更改字段的结果是,其他字段也可能会更改,具体取决于字段,字段值和日历系统。此外, get(f)在重新计算字段后不一定会返回值。具体情况由具体日历类确定。
<强> ADDED 强>
要检查“详细信息是否由具体日历类确定”为真,我检查了Dalvik和JDK 6的实际代码。
public void set(int field, int value) {
fields[field] = value;
isSet[field] = true;
areFieldsSet = isTimeSet = false;
if (field > MONTH && field < AM_PM) {
lastDateFieldSet = field;
}
if (field == HOUR || field == HOUR_OF_DAY) {
lastTimeFieldSet = field;
}
if (field == AM_PM) {
lastTimeFieldSet = HOUR;
}
}
public void set(int field, int value) {
if (isLenient() && areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false;
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
具体实现非常不同。 要找出问题的确切原因,您应该详细了解这两种实现。