这是 Android GregorianCalendar 中的错误吗?

Is this a bug in Android GregorianCalendar?

现在是 2015 年 1 月 26 日,我在 android 设备上有以下代码 运行;

GregorianCalendar date = new GregorianCalendar();
SimpleDateFormat df = new SimpleDateFormat();

// Set date to 4 weeks ago, and then use it for the first BETWEEN date in the above query    
date.set(GregorianCalendar.WEEK_OF_YEAR, date.get(GregorianCalendar.WEEK_OF_YEAR)-4);
System.out.println(df.format(new Date(date.getTimeInMillis())));
// Set date to 2 weeks time, and then use it for the second BETWEEN date in the above query

date.set(GregorianCalendar.WEEK_OF_YEAR, date.get(GregorianCalendar.WEEK_OF_YEAR)+6);
System.out.println(df.format(new Date(date.getTimeInMillis())));

我得到了输出;

29/12/14 10:29
09/02/14 10:29

运行 Windows 机器上的标准 Java 上的此代码段显示了正确的结果,第二个日期为 09/02/15 10:29。所以在 Android 上,当我们要求日期向后 4 周时,它正确地向后滚动年份,但是当我们要求它向前移动 6 周(超过我们的原始日期)时,它没有向后滚动年份再次转发

我在 5.0.2 和 4.4.2 上观察到了这个

所以问题是,这是一个错误还是某种令人费解的预期行为(特征)?

你不应该为此使用 Calendar.set。您正在尝试将 week 字段设置为无效值,并希望它能够理解您的预期含义。这可能适用于桌面 JVM,但你不应该依赖它 - 显然 - Android 的实现确实以不同的方式处理它。

改为使用 Calendar.add,它实际上可以满足您的需求:通过适当的调整在字段中添加或减去。

date.add(GregorianCalendar.WEEK_OF_YEAR, -4);
date.add(GregorianCalendar.WEEK_OF_YEAR, 6);

我很确定这是某些 Calendar 字段的计算和处理方式的复杂性,不是 错误。事实上,您使用算术调整而不是使用 add(...) 方法可能也会使事情复杂化。

来自文档...

The first week of a month or year is defined as the earliest seven day period beginning on getFirstDayOfWeek() and containing at least getMinimalDaysInFirstWeek() days of that month or year.

在这种情况下,一年的第一周,即 WEEK_OF_YEAR = 1,从 1 月 4 日或 5 日开始,具体取决于一周的第一天分别设置为星期日还是星期一。

Calendar class 是抽象的,具体 classes 的不同实现扩展它可能表现不同,但允许有 0 甚至 -1 数周的索引一年内。事实上,日期 1st -> 3rd 或 1st -> 4th(取决于一周的第一天)明显属于 2015 年,这意味着从 12 月 28 日或 29 日开始的那一周被 class 编辑为第 0 周。

1 月 26 日属于 WEEK_OF_YEAR = 4(不管一周的第一天),因此当您从当前 WEEK_OF_YEAR 中减去 4 时,它 returns 0 并调整日期到该周的星期一,即 12 月 29 日。

当您将之前调整的日期加上 6 周时,问题就来了 - 此时是 2014 年 12 月 29 日。正如我所说,您使用的是算术调整,而不是使用 add(...) 方法。由于您没有手动调整年份,它假定它应该保持在 2014 年内,并且通过添加 6 周您会导致溢出/回滚到 2014 年初,而不是动态调整 YEAR 以及WEEK_OF_YEAR