java - Java 和 Android 中 Calendar 类的不同行为

标签 java android calendar

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

我在 Java 控制台应用程序和 Android 模拟器中测试了以下代码 fragment ,它产生了不同的输出。

    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 类内部似乎有两个数据容器。

长时间保护

protected int[] 字段

所以当您调用 cal.set(Calendar.WEEK_OF_YEAR, 1) 时,您更改的是 fields 中的值,而不是 time类。

在 Java API 中

protected abstract void computeFields()

Converts the current millisecond time value time to calendar field values in fields[]. This allows you to sync up the calendar field values with a new time that is set for the calendar. The time is not recomputed first; to recompute the time, then the fields, call the complete() method.

我认为在 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[time=1348010308802,areFieldsSet=true,lenient=true,zone=org.apache.harmony.luni.internal.util.ZoneInfo["null",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,MILLISECOND==802,ZONE_OFFSET==0,DST_OFFSET==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],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==3,DAY_OF_WEEK_IN_MONTH==3,AM_PM==1,HOUR==11,HOUR_OF_DAY=23,MINUTE==18,SECOND==28,MILLISECOND==802,ZONE_OFFSET==0,DST_OFFSET==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],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,MILLISECOND==802,ZONE_OFFSET==0,DST_OFFSET==0]

2012.09.16

正如我们在上面看到的,字段中的值发生了变化,但是内部时间被表示为 ?,说明 time 没有与 fields< 同步

您使用 getTime() 方法获得了未同步的 time 并将其打印出来。

我认为 Android 中的日历旨在延迟同步,直到真正需要它为止。

添加

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

Calendar fields can be changed using three methods: set(), add(), and roll().

set(f, value) changes field f to value. In addition, it sets an internal member variable to indicate that field f has been changed. Although field f is changed immediately, the calendar's milliseconds is not recomputed until the next call to get(), getTime(), or getTimeInMillis() is made. Thus, multiple calls to set() do not trigger multiple, unnecessary computations. As a result of changing a field using set(), other fields may also change, depending on the field, the field value, and the calendar system. In addition, get(f) will not necessarily return value after the fields have been recomputed. The specifics are determined by the concrete calendar class.

添加

为了检查“具体由具体的日历类决定”是否属实,我查看了Dalvik和JDK 6的实际代码。

Dalvik 日历中的设置方法

来自 https://www.codeaurora.org/git/projects/qrd-gb-dsds-7225/repository/revisions/cc99b832a941dc8cbb86f1607d04eb87935ddbfd/entry/android/dalvik/libcore/luni/src/main/java/java/util/Calendar.java

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

JDK 6 的日历中的设置方法

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

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

关于java - Java 和 Android 中 Calendar 类的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12481505/

相关文章:

java - 检查特定日期后收到的传真

java - 在浏览器中使用Netty将短信与websocket绑定(bind)

java - 在java中使用前瞻的电子邮件正则表达式

java - NotifyBuilder 在重复运行时表现不一致

android - Android View 类的 onDraw 方法通过什么 Canvas ?

用 C 创建日历

java - SQLException :class org. apache.commons.dbutils.BeanProcessor(在模块commons.dbutils中)无法访问类Zhang,因为模块BeanListHandlerDemo

android - 在 eclipse 中复制或重命名 android 项目

android - 如何知道 ExoPlayer 何时可以播放?

ruby-on-rails - 如何将 Office 365 REST API 集成到我的 Ruby on Rails 应用程序中?