GregorianCalendar setFirstDayOfWeek 不影响牛轧糖前的 WEEK_OF_YEAR
GregorianCalendar setFirstDayOfWeek not affecting WEEK_OF_YEAR on pre Nougat
下面的代码给出了牛轧糖和预牛轧糖的不同结果。看一看,如果你愿意,可以自己尝试一下。如果有人能向我解释原因并提供解决方案,我将不胜感激。
我想要正确的 WEEK_OF_YEAR 值,取决于一周的第一天,适用于所有 Android 版本。我有一个时间 sheet 应用程序,我经常使用 gregorianCalendar,所以我不想切换到另一个 class/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???
如果您查看 release notes for Nougat,您会发现增强了对区域设置的支持。
特别是
Prior to Android 7.0, Android could not always successfully match app and system locales.
我也在自己的设备上注意到了这一点。我的 phone 设置为英语(澳大利亚)语言环境。在牛轧糖之前,
DateTimeFormat.forPattern("dd MMMM").print(new LocalDate(2017,1,29));
会打印 29 Jan
(没有 full-stop/period),但在 Nougat 之后它会打印 29 Jan.
(带句点)。
虽然我很难给出确切的细节,但您的情况似乎就是这样。 Post-Nougat,phone 能够更好地匹配应用程序和系统区域设置,包括您的区域设置的一周的第一天。在任何情况下,通过调试器逐步查找根本原因是行不通的,因为调用是在 libcore
内部处理的,而不是在源代码中公开的 Java class。
如果 Android 设备错误地报告了一年中 year/first 周的第一天,除了解决它之外,您几乎无能为力:
if (android.os.Build.VERSION.SDK_INT < 24) {
//pre-Nougat logic
}
else {
//Nougat/post-Nougat logic
}
您也可以尝试使用 enhanced replacement for GregorianCalendar(SDK 24 中添加)来解决您的问题。
如果您使用 class 类似 Joda-time or use the new JSR-310 classes (through the ThreeTen backport) you might be able to get what you want without having to work around GregorianCalendar
. In general these classes are much easier to use and less bug-prone. Many developers have already given up on java.util.Calendar
and java.util.Date
because of issues like this. Please see the answers to this canonical question 的详细信息
如果您要使用 Joda,则可以使用 LocalDate.fromCalendarFields(test)
将 GregorianCalendar
对象转换为 LocalDate
。这些 classes 使用 ISO 标准,其中一周的第一天始终是星期一。然后,您将在它们之上编写您想要的逻辑。那么 GregorianCalendar
的问题将 "isolated" 变成一个简单的问题,即检索给定区域设置的一年中的第一周。如果您的应用包含对服务器的调用,您可以改为通过 API 调用在一周的第一天提供服务。
更新:
注意时区行为已在 Android O 中更新:
其他语言环境和internationalization-related更改如下:
Time zone name parsing has changed. Previously, Android devices used the system clock value sampled at boot time to cache the time zone names used for parsing date times. As a result, parsing could be negatively affected if the system clock was wrong at boot time or in other, rarer cases.
Now, in common cases the parsing logic uses ICU and the current system clock value when parsing time zone names. This change provides more correct results, which may differ from earlier Android versions when your app uses classes like SimpleDateFormat. Android O updates the version of ICU to version 58.
我认为问题在于您设置月份的行。
//在月份参数中,您将 0 作为可能造成问题的值传递,因为即使数组从 0 开始,此 Calendar.MONTH 接受的输入也在 1 到 12 之间。所以尝试改变它。
或者您也可以将其更改为将其设置为一月作为使用月份
test.set(Calendar.MONTH, Calendar.JANUARY);
// 同样在这一行中尝试更改为
test.setFirstDayOfWeek(Calendar.SUNDAY);//Set it to Sunday
这应该可以解决您的问题。
下面的代码给出了牛轧糖和预牛轧糖的不同结果。看一看,如果你愿意,可以自己尝试一下。如果有人能向我解释原因并提供解决方案,我将不胜感激。
我想要正确的 WEEK_OF_YEAR 值,取决于一周的第一天,适用于所有 Android 版本。我有一个时间 sheet 应用程序,我经常使用 gregorianCalendar,所以我不想切换到另一个 class/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???
如果您查看 release notes for Nougat,您会发现增强了对区域设置的支持。
特别是
Prior to Android 7.0, Android could not always successfully match app and system locales.
我也在自己的设备上注意到了这一点。我的 phone 设置为英语(澳大利亚)语言环境。在牛轧糖之前,
DateTimeFormat.forPattern("dd MMMM").print(new LocalDate(2017,1,29));
会打印 29 Jan
(没有 full-stop/period),但在 Nougat 之后它会打印 29 Jan.
(带句点)。
虽然我很难给出确切的细节,但您的情况似乎就是这样。 Post-Nougat,phone 能够更好地匹配应用程序和系统区域设置,包括您的区域设置的一周的第一天。在任何情况下,通过调试器逐步查找根本原因是行不通的,因为调用是在 libcore
内部处理的,而不是在源代码中公开的 Java class。
如果 Android 设备错误地报告了一年中 year/first 周的第一天,除了解决它之外,您几乎无能为力:
if (android.os.Build.VERSION.SDK_INT < 24) {
//pre-Nougat logic
}
else {
//Nougat/post-Nougat logic
}
您也可以尝试使用 enhanced replacement for GregorianCalendar(SDK 24 中添加)来解决您的问题。
如果您使用 class 类似 Joda-time or use the new JSR-310 classes (through the ThreeTen backport) you might be able to get what you want without having to work around GregorianCalendar
. In general these classes are much easier to use and less bug-prone. Many developers have already given up on java.util.Calendar
and java.util.Date
because of issues like this. Please see the answers to this canonical question 的详细信息
如果您要使用 Joda,则可以使用 LocalDate.fromCalendarFields(test)
将 GregorianCalendar
对象转换为 LocalDate
。这些 classes 使用 ISO 标准,其中一周的第一天始终是星期一。然后,您将在它们之上编写您想要的逻辑。那么 GregorianCalendar
的问题将 "isolated" 变成一个简单的问题,即检索给定区域设置的一年中的第一周。如果您的应用包含对服务器的调用,您可以改为通过 API 调用在一周的第一天提供服务。
更新:
注意时区行为已在 Android O 中更新:
其他语言环境和internationalization-related更改如下:
Time zone name parsing has changed. Previously, Android devices used the system clock value sampled at boot time to cache the time zone names used for parsing date times. As a result, parsing could be negatively affected if the system clock was wrong at boot time or in other, rarer cases. Now, in common cases the parsing logic uses ICU and the current system clock value when parsing time zone names. This change provides more correct results, which may differ from earlier Android versions when your app uses classes like SimpleDateFormat. Android O updates the version of ICU to version 58.
我认为问题在于您设置月份的行。
//在月份参数中,您将 0 作为可能造成问题的值传递,因为即使数组从 0 开始,此 Calendar.MONTH 接受的输入也在 1 到 12 之间。所以尝试改变它。
或者您也可以将其更改为将其设置为一月作为使用月份
test.set(Calendar.MONTH, Calendar.JANUARY);
// 同样在这一行中尝试更改为
test.setFirstDayOfWeek(Calendar.SUNDAY);//Set it to Sunday
这应该可以解决您的问题。