GregorianCalendar setFirstDayOfWeek不影响前牛轧糖上的WEEK_OF_YEAR

Rob*_*hof 11 android dayofweek gregorian-calendar week-number

下面的代码给出了Nougat和pre-Nougat的不同结果.看看,如果你想要自己尝试一下.如果有人能解释我为什么并给出解决方案,我将不胜感激.

我想在所有Android版本上获得正确的WEEK_OF_YEAR值,具体取决于一周的第一天.我有一个时间表应用程序,我正在使用gregorianCalendar很多,所以我不想切换到另一个类/ lib.

    //default first day of the week is Monday for replication. I live in the Netherlands, it's weird.
    Locale l = new Locale("nl", "NL");

    GregorianCalendar test = new GregorianCalendar(l);
    test.set(Calendar.YEAR, 2017);
    test.set(Calendar.MONTH, 0);
    test.set(Calendar.DAY_OF_MONTH, 29);//this is a Sunday

    int week = test.get(Calendar.WEEK_OF_YEAR);//should be 4
    test.setFirstDayOfWeek(1);//Set it to Sunday
    int week2 = test.get(Calendar.WEEK_OF_YEAR);//should be 5 but is 4 below nougat???
Run Code Online (Sandbox Code Playgroud)

Dav*_*son 4

如果您查看Nougat 的发行说明,您会发现对区域设置的支持得到了增强。

尤其,

在 Android 7.0 之前,Android 无法始终成功匹配应用程序和系统区域设置。

我也在自己的设备上注意到了这一点。我的手机设置为英语(澳大利亚)区域设置。在牛轧糖之前,

DateTimeFormat.forPattern("dd MMMM").print(new LocalDate(2017,1,29));
Run Code Online (Sandbox Code Playgroud)

会打印29 Jan(没有句号/句号),但在牛轧糖之后它会打印29 Jan.(有句号)。

虽然我很难给出确切的细节,但似乎这就是你的情况所发生的情况。牛轧糖之后,手机能够更好地匹配应用程序和系统区域设置,包括您的区域设置一周的第一天。无论如何,使用调试器逐步查找根本原因是行不通的,因为调用是在内部处理的libcore,而不是在源代码中公开的 Java 类。

如果 Android 设备错误地报告一年中的第一天/一年中的第一周,除了解决此问题之外,您无能为力:

if (android.os.Build.VERSION.SDK_INT < 24) {
    //pre-Nougat logic
}
else {
    //Nougat/post-Nougat logic
}
Run Code Online (Sandbox Code Playgroud)

您或许还可以尝试使用GregorianCalendar 的增强替代品(在 SDK 24 中添加)来解决您的问题。

如果您使用像Joda-time这样的类或使用新的JSR-310类(通过 ThreeTen 向后移植),您可能无需解决GregorianCalendar. 一般来说,这些类更容易使用并且更不容易出现错误。许多开发人员已经因为此类问题java.util.Calendar而放弃了。有关详细信息,请参阅此规范问题的java.util.Date答案

如果您要使用 Joda,您可以使用LocalDate.fromCalendarFields(test)将对象转换GregorianCalendarLocalDate. 这些课程使用 ISO 标准,其中一周的第一天始终是星期一。然后你可以在它们之上编写你想要的逻辑。然后,问题GregorianCalendar将被“隔离”为仅检索给定区域设置的一年第一周的问题。如果您的应用程序包含对服务器的调用,则您可以通过 API 调用来提供一周的第一天。

更新:

请注意,时区行为已在Android O中更新:

其他与区域设置和国际化相关的更改如下:

时区名称解析已更改。以前,Android 设备使用启动时采样的系统时钟值来缓存用于解析日期时间的时区名称。因此,如果系统时钟在启动时或在其他罕见情况下出错,解析可能会受到负面影响。现在,在常见情况下,解析逻辑在解析时区名称时使用 ICU 和当前系统时钟值。此更改提供了更正确的结果,当您的应用使用 SimpleDateFormat 等类时,这可能与早期 Android 版本不同。Android O 将 ICU 版本更新至版本 58。