Java和Android中日历类的不同行为

Java和Android中日历类的不同行为,java,android,calendar,Java,Android,Calendar,我正在检索一年中第一周的日期,我发现了非常奇怪的行为 我在Java控制台应用程序和Android emulator中测试了以下代码片段,它产生了不同的输出 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.

我正在检索一年中第一周的日期,我发现了非常奇怪的行为

我在Java控制台应用程序和Android emulator中测试了以下代码片段,它产生了不同的输出

    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日志目录: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日志目录:2012/01/01(正确)

Java控制台:2012/01/01(正确)

我很想知道这件事


提前感谢。

似乎Calendar类内部有两个数据容器

长时间保护

受保护的int[]字段

因此,当您调用
cal.set(Calendar.WEEK,OF,OF,YEAR,1)
时,您可以更改类的
字段中的值,而不是
时间

在JavaAPI中

受保护的抽象void computeFields()

将当前毫秒时间值time转换为字段[]中的日历字段值。这允许您将日历字段值与为日历设置的新时间进行同步。时间不会首先重新计算;要重新计算时间,然后是字段,请调用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()));
日志:

java.util.GregorianCalendar[time=1348010308802,areFieldsSet=true,lenient=true,zone=org.apache.harmony.luni.internal.util.ZoneInfo[“null”,mRawOffset=0,mUseDst=false],星期一=1,星期一=4,纪元=1,年=2012,月=8,年中周=38,月中周=4,月中日=18,年中日=262,周中日=3,月中周中日=3,上午下午=1,小时=11,日中小时=23,分钟=18,秒偏移=0,毫秒=0]

2012.09.18

java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=org.apache.harmony.luni.internal.util.ZoneInfo[“null”,mRawOffset=0,mUseDst=false],星期一=1,星期一=4,纪元=1,年=2012,月=8,年中周=1,月中周=4,月中日=18,年中日=262,周中日=3,月中周中日=3,上午下午=1,小时=11,日中小时=23,分钟=18,秒偏移=0,毫秒=0]

2012.01.03

java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=org.apache.harmony.luni.internal.util.ZoneInfo[“null”,mRawOffset=0,mUseDst=false],星期一=1,星期一=4,纪元=1,年=2012,月=8,年中周=1,月中周=4,月中日=18,年中日=262,周中日=1,月中周中日=3,上午下午=1,小时=11,日中小时=23,分钟=18,秒偏移=0,毫秒=0]

2012.09.16

如上所述,字段中的值已更改,但内部时间表示为
,表示
时间
未与
字段
同步

您使用
getTime()
方法取消了时间的同步,并将其打印出来

我认为Android中的日历设计用于延迟同步,直到真正需要它

已添加

我在Java API中发现了以下内容:

可以使用三种方法更改日历字段:set()、add()和roll()

设置(f,值)将字段f更改为值。此外,它还设置了一个内部成员变量,以指示字段f已更改虽然字段f会立即更改,但在下一次调用get()、getTime()或getTimeInMillis()之前,不会重新计算日历的毫秒数。因此,多次调用set()不会触发多次不必要的计算。使用set()更改字段后,其他字段也可能会更改,具体取决于字段、字段值和日历系统。此外,get(f)在重新计算字段后不一定返回值。具体内容由具体日历类别决定。

已添加

为了检查“具体内容由具体的日历类决定”是否正确,我检查了Dalvik和JDK 6的实际代码

Dalvik日历中的Set方法 从

具体的实现是完全不同的。
要找出问题的确切原因,您应该详细查看这两种实现。

这非常有趣。一旦我将cal.get(Calendar.MONTH)放在年中的周行和确定周行之间,输出就正确了。有人能猜出原因吗?好问题。澄清一下,当你说“Java”时,你指的是Sun(嗯,Oracle)的实现?@Cheezmeister是的,我使用的是Sun(Oracle)JDKSo,我们可以说这是Android SDK中的一个bug吗?@VishalVyas,它不是bug。从我的答案中摘自JavaAPI:在重新计算字段后,get(f)不一定返回值<代码>具体由具体的calendar类决定。
@VishalVyas我检查了Dalvik calendar和JDK 6的calendar的实际代码,发现它们有不同的实现。我在我的答案中添加了更多关于这一点的内容。当我遇到这个问题时,calInst.getTimeInMillis()为我工作
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();
    }
}